Prepare and Preprocess Phase

Meta data

Fitbit data dictionary

Import libraries

library(tidyverse) # includes ggplot2
library(skimr) #  provides a compact and informative summary of your data frame or dataset
library(lubridate)
library(janitor) # set of utility functions for data cleaning and data frame tidying tasks
library(RColorBrewer) # Color palettes for data visualization
library(ggcorrplot) # Visualize correlation matrices using ggplot2
library(scales) # formatting and transforming data for visualizations


# display.brewer.all(colorblindFriendly = TRUE)

Load datasets

# Clean environment
rm(list = ls())

daily_activity <-
  read_csv("original_data/dailyActivity_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )

daily_sleep <- read_csv("original_data/sleepDay_merged.csv",
  trim_ws = TRUE,
  show_col_types = FALSE
)

hourly_calories <-
  read_csv("original_data/hourlyCalories_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )

hourly_intensities <-
  read_csv("original_data/hourlyIntensities_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )
hourly_steps <-
  read_csv("original_data/hourlySteps_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )

minute_sleep <-
  read_csv("original_data/minuteSleep_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )

weight_logs <-
  read_csv("original_data/weightLogInfo_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )

seconds_heartrate <-
  read_csv("original_data/heartrate_seconds_merged.csv",
    trim_ws = TRUE,
    show_col_types = FALSE
  )

# Remove trailing spaces (trim_ws = TRUE)

Clean data sets

Clean the daily_activity data set

# Check daily_activity data set before cleaning
glimpse(daily_activity)
Rows: 940
Columns: 15
$ Id                       <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 150…
$ ActivityDate             <chr> "4/12/2016", "4/13/2016", "4/14/2016", "4/15/2016", "4/16/2016", "4/17/2016", "4/18/2016", "4/19/20…
$ TotalSteps               <dbl> 13162, 10735, 10460, 9762, 12669, 9705, 13019, 15506, 10544, 9819, 12764, 14371, 10039, 15355, 1375…
$ TotalDistance            <dbl> 8.50, 6.97, 6.74, 6.28, 8.16, 6.48, 8.59, 9.88, 6.68, 6.34, 8.13, 9.04, 6.41, 9.80, 8.79, 12.21, 8.…
$ TrackerDistance          <dbl> 8.50, 6.97, 6.74, 6.28, 8.16, 6.48, 8.59, 9.88, 6.68, 6.34, 8.13, 9.04, 6.41, 9.80, 8.79, 12.21, 8.…
$ LoggedActivitiesDistance <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ VeryActiveDistance       <dbl> 1.88, 1.57, 2.44, 2.14, 2.71, 3.19, 3.25, 3.53, 1.96, 1.34, 4.76, 2.81, 2.92, 5.29, 2.33, 6.40, 3.5…
$ ModeratelyActiveDistance <dbl> 0.55, 0.69, 0.40, 1.26, 0.41, 0.78, 0.64, 1.32, 0.48, 0.35, 1.12, 0.87, 0.21, 0.57, 0.92, 0.41, 1.1…
$ LightActiveDistance      <dbl> 6.06, 4.71, 3.91, 2.83, 5.04, 2.51, 4.71, 5.03, 4.24, 4.65, 2.24, 5.36, 3.28, 3.94, 5.54, 5.41, 3.7…
$ SedentaryActiveDistance  <dbl> 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.0…
$ VeryActiveMinutes        <dbl> 25, 21, 30, 29, 36, 38, 42, 50, 28, 19, 66, 41, 39, 73, 31, 78, 48, 16, 52, 33, 41, 50, 36, 45, 24,…
$ FairlyActiveMinutes      <dbl> 13, 19, 11, 34, 10, 20, 16, 31, 12, 8, 27, 21, 5, 14, 23, 11, 28, 12, 34, 35, 15, 24, 22, 24, 6, 46…
$ LightlyActiveMinutes     <dbl> 328, 217, 181, 209, 221, 164, 233, 264, 205, 211, 130, 262, 238, 216, 279, 243, 189, 243, 217, 246,…
$ SedentaryMinutes         <dbl> 728, 776, 1218, 726, 773, 539, 1149, 775, 818, 838, 1217, 732, 709, 814, 833, 1108, 782, 815, 712, …
$ Calories                 <dbl> 1985, 1797, 1776, 1745, 1863, 1728, 1921, 2035, 1786, 1775, 1827, 1949, 1788, 2013, 1970, 2159, 189…
# Check missing values and duplicates
cat(
  "\n",
  "Missing values:",
  sum(is.na(daily_activity)),
  "\n",
  "Duplicate values:",
  sum(duplicated(daily_activity)),
  "\n",
  "Unique Ids:",
  n_distinct(daily_activity$Id)
)

 Missing values: 0 
 Duplicate values: 0 
 Unique Ids: 33

Let us clean: - Change column names to lower case because R is case sensitive - Change “Id” from double to a character because the number represents a category - Change “ActivityDate” from char to date

# Clean daily_activity data set

daily_activity <-
  # Clean column names
  clean_names(daily_activity) %>%
  # Correct column types
  mutate(id = as.character(id)) %>% # from double to chr
  mutate(activity_date = as.Date(activity_date,
                                 format = "%m/%d/%Y")) %>% # from chr to date
  # Remove duplicate rows
  distinct()

# Check daily_activity data set after cleaning
glimpse(daily_activity)
Rows: 940
Columns: 15
$ id                         <chr> "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960366",…
$ activity_date              <date> 2016-04-12, 2016-04-13, 2016-04-14, 2016-04-15, 2016-04-16, 2016-04-17, 2016-04-18, 2016-04-19, …
$ total_steps                <dbl> 13162, 10735, 10460, 9762, 12669, 9705, 13019, 15506, 10544, 9819, 12764, 14371, 10039, 15355, 13…
$ total_distance             <dbl> 8.50, 6.97, 6.74, 6.28, 8.16, 6.48, 8.59, 9.88, 6.68, 6.34, 8.13, 9.04, 6.41, 9.80, 8.79, 12.21, …
$ tracker_distance           <dbl> 8.50, 6.97, 6.74, 6.28, 8.16, 6.48, 8.59, 9.88, 6.68, 6.34, 8.13, 9.04, 6.41, 9.80, 8.79, 12.21, …
$ logged_activities_distance <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ very_active_distance       <dbl> 1.88, 1.57, 2.44, 2.14, 2.71, 3.19, 3.25, 3.53, 1.96, 1.34, 4.76, 2.81, 2.92, 5.29, 2.33, 6.40, 3…
$ moderately_active_distance <dbl> 0.55, 0.69, 0.40, 1.26, 0.41, 0.78, 0.64, 1.32, 0.48, 0.35, 1.12, 0.87, 0.21, 0.57, 0.92, 0.41, 1…
$ light_active_distance      <dbl> 6.06, 4.71, 3.91, 2.83, 5.04, 2.51, 4.71, 5.03, 4.24, 4.65, 2.24, 5.36, 3.28, 3.94, 5.54, 5.41, 3…
$ sedentary_active_distance  <dbl> 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0…
$ very_active_minutes        <dbl> 25, 21, 30, 29, 36, 38, 42, 50, 28, 19, 66, 41, 39, 73, 31, 78, 48, 16, 52, 33, 41, 50, 36, 45, 2…
$ fairly_active_minutes      <dbl> 13, 19, 11, 34, 10, 20, 16, 31, 12, 8, 27, 21, 5, 14, 23, 11, 28, 12, 34, 35, 15, 24, 22, 24, 6, …
$ lightly_active_minutes     <dbl> 328, 217, 181, 209, 221, 164, 233, 264, 205, 211, 130, 262, 238, 216, 279, 243, 189, 243, 217, 24…
$ sedentary_minutes          <dbl> 728, 776, 1218, 726, 773, 539, 1149, 775, 818, 838, 1217, 732, 709, 814, 833, 1108, 782, 815, 712…
$ calories                   <dbl> 1985, 1797, 1776, 1745, 1863, 1728, 1921, 2035, 1786, 1775, 1827, 1949, 1788, 2013, 1970, 2159, 1…
# Check missing values and duplicates after cleaning
cat("\n",
    "Missing values:",
    sum(is.na(daily_activity)),
    "\n",
    "Duplicate values:",
    sum(duplicated(daily_activity)))

 Missing values: 0 
 Duplicate values: 0
# Let us print summary statistic to have a better idea of the data set
daily_activity %>%
  summary()
      id            activity_date         total_steps    total_distance   tracker_distance logged_activities_distance
 Length:940         Min.   :2016-04-12   Min.   :    0   Min.   : 0.000   Min.   : 0.000   Min.   :0.0000            
 Class :character   1st Qu.:2016-04-19   1st Qu.: 3790   1st Qu.: 2.620   1st Qu.: 2.620   1st Qu.:0.0000            
 Mode  :character   Median :2016-04-26   Median : 7406   Median : 5.245   Median : 5.245   Median :0.0000            
                    Mean   :2016-04-26   Mean   : 7638   Mean   : 5.490   Mean   : 5.475   Mean   :0.1082            
                    3rd Qu.:2016-05-04   3rd Qu.:10727   3rd Qu.: 7.713   3rd Qu.: 7.710   3rd Qu.:0.0000            
                    Max.   :2016-05-12   Max.   :36019   Max.   :28.030   Max.   :28.030   Max.   :4.9421            
 very_active_distance moderately_active_distance light_active_distance sedentary_active_distance very_active_minutes
 Min.   : 0.000       Min.   :0.0000             Min.   : 0.000        Min.   :0.000000          Min.   :  0.00     
 1st Qu.: 0.000       1st Qu.:0.0000             1st Qu.: 1.945        1st Qu.:0.000000          1st Qu.:  0.00     
 Median : 0.210       Median :0.2400             Median : 3.365        Median :0.000000          Median :  4.00     
 Mean   : 1.503       Mean   :0.5675             Mean   : 3.341        Mean   :0.001606          Mean   : 21.16     
 3rd Qu.: 2.053       3rd Qu.:0.8000             3rd Qu.: 4.782        3rd Qu.:0.000000          3rd Qu.: 32.00     
 Max.   :21.920       Max.   :6.4800             Max.   :10.710        Max.   :0.110000          Max.   :210.00     
 fairly_active_minutes lightly_active_minutes sedentary_minutes    calories   
 Min.   :  0.00        Min.   :  0.0          Min.   :   0.0    Min.   :   0  
 1st Qu.:  0.00        1st Qu.:127.0          1st Qu.: 729.8    1st Qu.:1828  
 Median :  6.00        Median :199.0          Median :1057.5    Median :2134  
 Mean   : 13.56        Mean   :192.8          Mean   : 991.2    Mean   :2304  
 3rd Qu.: 19.00        3rd Qu.:264.0          3rd Qu.:1229.5    3rd Qu.:2793  
 Max.   :143.00        Max.   :518.0          Max.   :1440.0    Max.   :4900  

This summary helps us explore quickly each attribute. We notice that some attributes have minimum value of zero (total_step, total_distance, calories). Let us explore this observation.

# Check where total_steps is zero
filter(daily_activity, total_steps == 0)

We found 77 observations where total_steps is zero. We should delete these observations so that they do not affect our the mean and median. If total_step is zero that means that the person did not wear the Fitbit.

# Check where calories is zero
filter(daily_activity, calories == 0)
# Check where total_distance is zero
filter(daily_activity, total_distance == 0)

From our inspection above, we can see that we just need to delete the entries where total_steps is zero and will take take care of the rest.

daily_activity_clean <-
  filter(daily_activity,
         total_steps != 0,
         total_distance != 0,
         calories != 0)
daily_activity_clean
NA
names(daily_activity)
 [1] "id"                         "activity_date"              "total_steps"                "total_distance"            
 [5] "tracker_distance"           "logged_activities_distance" "very_active_distance"       "moderately_active_distance"
 [9] "light_active_distance"      "sedentary_active_distance"  "very_active_minutes"        "fairly_active_minutes"     
[13] "lightly_active_minutes"     "sedentary_minutes"          "calories"                  
# Check the attributes again

cat("Before deleting the entries\n\n")
Before deleting the entries
select(daily_activity,total_steps,total_distance,calories) %>%
  summary()
  total_steps    total_distance      calories   
 Min.   :    0   Min.   : 0.000   Min.   :   0  
 1st Qu.: 3790   1st Qu.: 2.620   1st Qu.:1828  
 Median : 7406   Median : 5.245   Median :2134  
 Mean   : 7638   Mean   : 5.490   Mean   :2304  
 3rd Qu.:10727   3rd Qu.: 7.713   3rd Qu.:2793  
 Max.   :36019   Max.   :28.030   Max.   :4900  
cat("\n\n\n",
    "\t\t vs",
    "\n\n\n")



         vs 
cat("After deleting the entries\n\n")
After deleting the entries
select(daily_activity_clean, total_steps, total_distance, calories) %>%
  summary()
  total_steps    total_distance      calories   
 Min.   :    8   Min.   : 0.010   Min.   :  52  
 1st Qu.: 4927   1st Qu.: 3.373   1st Qu.:1857  
 Median : 8054   Median : 5.590   Median :2220  
 Mean   : 8329   Mean   : 5.986   Mean   :2362  
 3rd Qu.:11096   3rd Qu.: 7.905   3rd Qu.:2832  
 Max.   :36019   Max.   :28.030   Max.   :4900  

We can see that the observation we removed affected our mean and median.

Clean the daily_sleep data set

# Check daily_sleep data set before cleaning
glimpse(daily_sleep)
Rows: 413
Columns: 5
$ Id                 <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 150396036…
$ SleepDay           <chr> "4/12/2016 12:00:00 AM", "4/13/2016 12:00:00 AM", "4/15/2016 12:00:00 AM", "4/16/2016 12:00:00 AM", "4/17…
$ TotalSleepRecords  <dbl> 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 2, …
$ TotalMinutesAsleep <dbl> 327, 384, 412, 340, 700, 304, 360, 325, 361, 430, 277, 245, 366, 341, 404, 369, 277, 273, 247, 334, 331, …
$ TotalTimeInBed     <dbl> 346, 407, 442, 367, 712, 320, 377, 364, 384, 449, 323, 274, 393, 354, 425, 396, 309, 296, 264, 367, 349, …
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(daily_sleep)),
    "\n",
    "Duplicate values:",
    sum(duplicated(daily_sleep)),
  "\n",
  "Unique Ids:",
  n_distinct(daily_sleep$Id)
    )

 Missing values: 0 
 Duplicate values: 3 
 Unique Ids: 24

Let us clean:

  • Change column names to lower case because R is case sensitive
  • Change “Id” from double to a character because the number represents a category
  • Change “SleepDay” from char to date. Since the time component of this column is the same for each observation”12:00:00 AM”, we can remove it. This will helps us merged this data set with daily_activity later
  • Delete duplicates (3 observations are duplicates)
# Clean daily_sleep data set

daily_sleep_clean <-
  # Clean column names
  clean_names(daily_sleep) %>%
  # Correct column types
  mutate(id = as.character(id)) %>% # from double to chr
  mutate(sleep_day = as.Date(sleep_day,
                             format = "%m/%d/%Y")) %>% # from chr to date
  # Remove duplicate rows
  distinct()

# Check clean daily_sleep data set
glimpse(daily_sleep_clean)
Rows: 410
Columns: 5
$ id                   <chr> "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503…
$ sleep_day            <date> 2016-04-12, 2016-04-13, 2016-04-15, 2016-04-16, 2016-04-17, 2016-04-19, 2016-04-20, 2016-04-21, 2016-0…
$ total_sleep_records  <dbl> 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 2…
$ total_minutes_asleep <dbl> 327, 384, 412, 340, 700, 304, 360, 325, 361, 430, 277, 245, 366, 341, 404, 369, 277, 273, 247, 334, 331…
$ total_time_in_bed    <dbl> 346, 407, 442, 367, 712, 320, 377, 364, 384, 449, 323, 274, 393, 354, 425, 396, 309, 296, 264, 367, 349…
# Check missing values and duplicates after cleaning
cat("\n",
    "Missing values:",
    sum(is.na(daily_sleep_clean)),
    "\n",
    "Duplicate values:",
    sum(duplicated(daily_sleep_clean)))

 Missing values: 0 
 Duplicate values: 0

Clean the hourly data sets (hourly_calories, hourly_intensities, and hourly_steps)

# Check hourly_calories data set before cleaning
glimpse(hourly_calories)
Rows: 22,099
Columns: 3
$ Id           <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 150…
$ ActivityHour <chr> "4/12/2016 12:00:00 AM", "4/12/2016 1:00:00 AM", "4/12/2016 2:00:00 AM", "4/12/2016 3:00:00 AM", "4/12/2016 4:0…
$ Calories     <dbl> 81, 61, 59, 47, 48, 48, 48, 47, 68, 141, 99, 76, 73, 66, 110, 151, 76, 83, 124, 104, 132, 100, 65, 81, 69, 48, …
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(hourly_calories)),
    "\n",
    "Duplicate values:",
    sum(duplicated(hourly_calories)))

 Missing values: 0 
 Duplicate values: 0
# Check hourly_intensities data set before cleaning
glimpse(hourly_intensities)
Rows: 22,099
Columns: 4
$ Id               <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366,…
$ ActivityHour     <chr> "4/12/2016 12:00:00 AM", "4/12/2016 1:00:00 AM", "4/12/2016 2:00:00 AM", "4/12/2016 3:00:00 AM", "4/12/2016…
$ TotalIntensity   <dbl> 20, 8, 7, 0, 0, 0, 0, 0, 13, 30, 29, 12, 11, 6, 36, 58, 13, 16, 29, 39, 41, 31, 9, 21, 14, 0, 0, 4, 0, 0, 0…
$ AverageIntensity <dbl> 0.333333, 0.133333, 0.116667, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.216667, 0.500000, 0.48333…
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(hourly_intensities)),
    "\n",
    "Duplicate values:",
    sum(duplicated(hourly_intensities)))

 Missing values: 0 
 Duplicate values: 0
# Check hourly_steps data set before cleaning
glimpse(hourly_steps)
Rows: 22,099
Columns: 3
$ Id           <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 150…
$ ActivityHour <chr> "4/12/2016 12:00:00 AM", "4/12/2016 1:00:00 AM", "4/12/2016 2:00:00 AM", "4/12/2016 3:00:00 AM", "4/12/2016 4:0…
$ StepTotal    <dbl> 373, 160, 151, 0, 0, 0, 0, 0, 250, 1864, 676, 360, 253, 221, 1166, 2063, 344, 489, 1386, 558, 1733, 684, 89, 33…
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(hourly_steps)),
    "\n",
    "Duplicate values:",
    sum(duplicated(hourly_steps)))

 Missing values: 0 
 Duplicate values: 0

Join hourly data sets to create a hourly_actitvity data set

These data sets shared the same Id and Activity_hour, let us join them into a new data set (hourly_activity) before we clean them.

# Join the hourly data sets (hourly_calories, hourly_intensities, and hourly_steps)

hourly_activity <-
  inner_join(hourly_calories,
             hourly_intensities,
             by = c("Id", "ActivityHour"))

hourly_activity <-
  inner_join(hourly_activity, hourly_steps, by = c("Id", "ActivityHour"))
# Check hourly_activity data set before cleaning
glimpse(hourly_activity)
Rows: 22,099
Columns: 6
$ Id               <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366,…
$ ActivityHour     <chr> "4/12/2016 12:00:00 AM", "4/12/2016 1:00:00 AM", "4/12/2016 2:00:00 AM", "4/12/2016 3:00:00 AM", "4/12/2016…
$ Calories         <dbl> 81, 61, 59, 47, 48, 48, 48, 47, 68, 141, 99, 76, 73, 66, 110, 151, 76, 83, 124, 104, 132, 100, 65, 81, 69, …
$ TotalIntensity   <dbl> 20, 8, 7, 0, 0, 0, 0, 0, 13, 30, 29, 12, 11, 6, 36, 58, 13, 16, 29, 39, 41, 31, 9, 21, 14, 0, 0, 4, 0, 0, 0…
$ AverageIntensity <dbl> 0.333333, 0.133333, 0.116667, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.216667, 0.500000, 0.48333…
$ StepTotal        <dbl> 373, 160, 151, 0, 0, 0, 0, 0, 250, 1864, 676, 360, 253, 221, 1166, 2063, 344, 489, 1386, 558, 1733, 684, 89…
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(hourly_activity)),
    "\n",
    "Duplicate values:",
    sum(duplicated(hourly_activity)))

 Missing values: 0 
 Duplicate values: 0

Let us clean:

  • Change column names to lower case because R is case sensitive
  • Change “Id” from double to a character because the number represents a category
  • Change “ActivityHour” from char to datetime

Note:The default timezone is UTC.

# Clean hourly_activity data set

hourly_activity_clean <-
  # Clean column names
  clean_names(hourly_activity) %>%
  # Correct column types
  mutate(id = as.character(id)) %>% # from double to chr
  mutate(activity_hour = as_datetime(activity_hour,
                                     format = "%m/%d/%Y %I:%M:%S %p")) %>% # from chr to datetime
  # Remove duplicate rows
  distinct()

# Check clean daily_activity data set
glimpse(hourly_activity_clean)
Rows: 22,099
Columns: 6
$ id                <chr> "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960…
$ activity_hour     <dttm> 2016-04-12 00:00:00, 2016-04-12 01:00:00, 2016-04-12 02:00:00, 2016-04-12 03:00:00, 2016-04-12 04:00:00, …
$ calories          <dbl> 81, 61, 59, 47, 48, 48, 48, 47, 68, 141, 99, 76, 73, 66, 110, 151, 76, 83, 124, 104, 132, 100, 65, 81, 69,…
$ total_intensity   <dbl> 20, 8, 7, 0, 0, 0, 0, 0, 13, 30, 29, 12, 11, 6, 36, 58, 13, 16, 29, 39, 41, 31, 9, 21, 14, 0, 0, 4, 0, 0, …
$ average_intensity <dbl> 0.333333, 0.133333, 0.116667, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.216667, 0.500000, 0.4833…
$ step_total        <dbl> 373, 160, 151, 0, 0, 0, 0, 0, 250, 1864, 676, 360, 253, 221, 1166, 2063, 344, 489, 1386, 558, 1733, 684, 8…
# Check missing values and duplicates after cleaning
cat("\n",
    "Missing values:",
    sum(is.na(hourly_activity_clean)),
    "\n",
    "Duplicate values:",
    sum(duplicated(hourly_activity_clean)))

 Missing values: 0 
 Duplicate values: 0
# as_datetime() converts with default timezone = "UTC"

Clean the minute_sleep data set

# Check minute_sleep data set before cleaning
glimpse(minute_sleep)
Rows: 188,521
Columns: 4
$ Id    <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366, 1503960366…
$ date  <chr> "4/12/2016 2:47:30 AM", "4/12/2016 2:48:30 AM", "4/12/2016 2:49:30 AM", "4/12/2016 2:50:30 AM", "4/12/2016 2:51:30 AM"…
$ value <dbl> 3, 2, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ logId <dbl> 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 1…
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(minute_sleep)),
    "\n",
    "Duplicate values:",
    sum(duplicated(minute_sleep)),
    "\n",
  "Unique Ids:",
  n_distinct(minute_sleep$Id))

 Missing values: 0 
 Duplicate values: 543 
 Unique Ids: 24

Let us clean:

  • Change column names to lower case because R is case sensitive
  • Change “Id” from double to a character because the number represents a category
  • Change “date” from char to datetime
  • Change “value” from double to factor. Value indicates the sleep state: 1 = asleep, 2 = restless, 3 = awake. See: Fitbit data dictionary
  • Remove duplicate values: 543
# Clean minute_sleep data set

minute_sleep_clean <-
  # Clean column names
  clean_names(minute_sleep) %>%
  # Correct column types
  mutate(value = as.factor(value)) %>% # from double to chr
  mutate(id = as.character(id)) %>% # from double to chr
  mutate(date = as_datetime(date,
                            format = "%m/%d/%Y %I:%M:%S %p")) %>% # From chr to datetime
  # Remove duplicate rows
  distinct()

# Check clean daily_activity data set
glimpse(minute_sleep_clean)
Rows: 187,978
Columns: 4
$ id     <chr> "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503960366", "1503…
$ date   <dttm> 2016-04-12 02:47:30, 2016-04-12 02:48:30, 2016-04-12 02:49:30, 2016-04-12 02:50:30, 2016-04-12 02:51:30, 2016-04-12 …
$ value  <fct> 3, 2, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
$ log_id <dbl> 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, 11380564589, …
# Check missing values and duplicates after cleaning
cat("\n",
    "Missing values:",
    sum(is.na(minute_sleep_clean)),
    "\n",
    "Duplicate values:",
    sum(duplicated(minute_sleep_clean)))

 Missing values: 0 
 Duplicate values: 0

Clean the seconds_heartrate data set

# Check seconds_heartrate set before cleaning
glimpse(seconds_heartrate)
Rows: 2,483,658
Columns: 3
$ Id    <dbl> 2022484408, 2022484408, 2022484408, 2022484408, 2022484408, 2022484408, 2022484408, 2022484408, 2022484408, 2022484408…
$ Time  <chr> "4/12/2016 7:21:00 AM", "4/12/2016 7:21:05 AM", "4/12/2016 7:21:10 AM", "4/12/2016 7:21:20 AM", "4/12/2016 7:21:25 AM"…
$ Value <dbl> 97, 102, 105, 103, 101, 95, 91, 93, 94, 93, 92, 89, 83, 61, 60, 61, 61, 57, 54, 55, 58, 60, 59, 57, 56, 58, 57, 58, 60…
# Check missing values and duplicates
cat(
  "\n",
  "Missing values:", sum(is.na(seconds_heartrate)),
  "\n",
  "Duplicate values:", sum(duplicated(seconds_heartrate))
)

 Missing values: 0 
 Duplicate values: 0

Let us clean:

  • Change column names to lower case because R is case sensitive
  • Change “Id” from double to a character because the number represents a category
  • Change “Time” from char to datetime and rename it date_time
  • Rename “Value” to heart_rate Fitbit data dictionary
# Clean seconds_heartrate data set

seconds_heartrate_clean <-
  # Clean column names
  clean_names(seconds_heartrate) %>%
  # Correct column types
  mutate(id = as.character(id)) %>% # from double to chr
  mutate(time = as_datetime(time,
                            format = "%m/%d/%Y %I:%M:%S %p")) %>% # from chr to datetime
  # Rename columns
  rename(date_time = time,
         heart_rate = value) %>%
  # Remove duplicate rows
  distinct()

# Check clean daily_activity data set
glimpse(seconds_heartrate_clean)
Rows: 2,483,658
Columns: 3
$ id         <chr> "2022484408", "2022484408", "2022484408", "2022484408", "2022484408", "2022484408", "2022484408", "2022484408", "…
$ date_time  <dttm> 2016-04-12 07:21:00, 2016-04-12 07:21:05, 2016-04-12 07:21:10, 2016-04-12 07:21:20, 2016-04-12 07:21:25, 2016-04…
$ heart_rate <dbl> 97, 102, 105, 103, 101, 95, 91, 93, 94, 93, 92, 89, 83, 61, 60, 61, 61, 57, 54, 55, 58, 60, 59, 57, 56, 58, 57, 5…
# Check missing values and duplicates after cleaning

cat("\n",
    "Missing values:",
    sum(is.na(seconds_heartrate_clean)),
    "\n",
    "Duplicate values:",
    sum(duplicated(seconds_heartrate_clean)))

 Missing values: 0 
 Duplicate values: 0
# as_datetime() converts with default timezone = "UTC"

Clean the weight_logs data set

# Check weight_logs set before cleaning

glimpse(weight_logs)
Rows: 67
Columns: 8
$ Id             <dbl> 1503960366, 1503960366, 1927972279, 2873212765, 2873212765, 4319703577, 4319703577, 4558609924, 4558609924, 4558609924, 455…
$ Date           <chr> "5/2/2016 11:59:59 PM", "5/3/2016 11:59:59 PM", "4/13/2016 1:08:52 AM", "4/21/2016 11:59:59 PM", "5/12/2016 11:59:59 PM", "…
$ WeightKg       <dbl> 52.6, 52.6, 133.5, 56.7, 57.3, 72.4, 72.3, 69.7, 70.3, 69.9, 69.2, 69.1, 90.7, 62.5, 62.1, 61.7, 61.5, 62.0, 61.4, 61.2, 61…
$ WeightPounds   <dbl> 115.9631, 115.9631, 294.3171, 125.0021, 126.3249, 159.6147, 159.3942, 153.6622, 154.9850, 154.1031, 152.5599, 152.3394, 199…
$ Fat            <dbl> 22, NA, NA, NA, NA, 25, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ BMI            <dbl> 22.65, 22.65, 47.54, 21.45, 21.69, 27.45, 27.38, 27.25, 27.46, 27.32, 27.04, 27.00, 28.00, 24.39, 24.24, 24.10, 24.00, 24.2…
$ IsManualReport <lgl> TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, T…
$ LogId          <dbl> 1.462234e+12, 1.462320e+12, 1.460510e+12, 1.461283e+12, 1.463098e+12, 1.460938e+12, 1.462406e+12, 1.461024e+12, 1.461629e+1…
# Check missing values and duplicates
cat("\n",
    "Missing values:",
    sum(is.na(weight_logs)),
    "\n",
    "Duplicate values:",
    sum(duplicated(weight_logs)))

 Missing values: 65 
 Duplicate values: 0

Let us clean: - Change column names to lower case because R is case sensitive - Change “Id” from double to a character because the number represents a category - Change “Date” from char to datetime and rename it date_time - Change NA to 0 in the column “fat”

# Clean  weight_logs data set

weight_logs_clean <-
  # Clean column names
  clean_names(weight_logs) %>%
  # Correct column types
  mutate(id = as.character(id)) %>% # from double to chr
  mutate(date = as_datetime(date,
                            format = "%m/%d/%Y %I:%M:%S %p")) %>% # from chr to datetime
  # Rename columns
  rename(date_time = date) %>%
  # Remove duplicate rows
  distinct()



# Change NA to 0 in the column "fat"
weight_logs_clean$fat[is.na(weight_logs_clean$fat)] <- 0

# Check clean daily_activity data set
glimpse(weight_logs_clean)
Rows: 67
Columns: 8
$ id               <chr> "1503960366", "1503960366", "1927972279", "2873212765", "2873212765", "4319703577", "4319703577", "4558609924", "45586099…
$ date_time        <dttm> 2016-05-02 23:59:59, 2016-05-03 23:59:59, 2016-04-13 01:08:52, 2016-04-21 23:59:59, 2016-05-12 23:59:59, 2016-04-17 23:5…
$ weight_kg        <dbl> 52.6, 52.6, 133.5, 56.7, 57.3, 72.4, 72.3, 69.7, 70.3, 69.9, 69.2, 69.1, 90.7, 62.5, 62.1, 61.7, 61.5, 62.0, 61.4, 61.2, …
$ weight_pounds    <dbl> 115.9631, 115.9631, 294.3171, 125.0021, 126.3249, 159.6147, 159.3942, 153.6622, 154.9850, 154.1031, 152.5599, 152.3394, 1…
$ fat              <dbl> 22, 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ bmi              <dbl> 22.65, 22.65, 47.54, 21.45, 21.69, 27.45, 27.38, 27.25, 27.46, 27.32, 27.04, 27.00, 28.00, 24.39, 24.24, 24.10, 24.00, 24…
$ is_manual_report <lgl> TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,…
$ log_id           <dbl> 1.462234e+12, 1.462320e+12, 1.460510e+12, 1.461283e+12, 1.463098e+12, 1.460938e+12, 1.462406e+12, 1.461024e+12, 1.461629e…
# Check missing values and duplicates after cleaning

cat("\n",
    "Missing values:",
    sum(is.na(weight_logs_clean)),
    "\n",
    "Duplicate values:",
    sum(duplicated(weight_logs_clean)))

 Missing values: 0 
 Duplicate values: 0

Distribution of ids across data sets

# Loop through each  data set and print th number o funiqu ids
datasets <- c(
    "daily_activity_clean",
    "daily_sleep_clean",
    "hourly_activity_clean",
    "minute_sleep_clean",
    "seconds_heartrate_clean",
    "weight_logs_clean"
)


results_df <- data.frame(Dataset = character(0), distinct_IDs = integer(0))

for (dataset_name in datasets) {
    dataset <- get(dataset_name)  # Retrieve the dataset by its name
    distinct_ids <- length(unique(dataset$id))  # Calculate the number of distinct IDs
    
    result_row <- data.frame(Dataset = dataset_name, distinct_IDs = distinct_ids)
    results_df <- bind_rows(results_df, result_row)
}

sorted_results <- results_df %>% arrange(- distinct_IDs )

print(sorted_results)
NA

Differences in the number of unique IDs between the data sets can imply discrepancies in data collection methods, data incompleteness, or differing levels of user engagement.

Export clean data sets

# To uncomment the following code, select all the lines and press shift + control + c on Mac


# write.csv(daily_activity_clean, 
#           "daily_activity_clean.csv", 
#           row.names = FALSE)
# 
# write.csv(daily_sleep_clean, 
#           "daily_sleep_clean.csv", 
#           row.names = FALSE)
# 
# write.csv(daily_sleep_clean, 
#           "hourly_activity_clean.csv", 
#           row.names = FALSE)
# 
# write.csv(minute_sleep_clean, 
#           "minute_sleep_clean.csv",
#           row.names = FALSE)
# 
# write.csv(seconds_heartrate_clean,
#           "seconds_heartrate_clean.csv",
#           row.names = FALSE)
# 
# write.csv(weight_logs_clean , 
#           "weight_logs_clean .csv",
#           row.names = FALSE)

Analyze Phase: Exploratory Data Analysis

EDA for daily_activity_clean

str(daily_activity_clean)
tibble [862 × 15] (S3: tbl_df/tbl/data.frame)
 $ id                        : chr [1:862] "1503960366" "1503960366" "1503960366" "1503960366" ...
 $ activity_date             : Date[1:862], format: "2016-04-12" "2016-04-13" "2016-04-14" "2016-04-15" ...
 $ total_steps               : num [1:862] 13162 10735 10460 9762 12669 ...
 $ total_distance            : num [1:862] 8.5 6.97 6.74 6.28 8.16 ...
 $ tracker_distance          : num [1:862] 8.5 6.97 6.74 6.28 8.16 ...
 $ logged_activities_distance: num [1:862] 0 0 0 0 0 0 0 0 0 0 ...
 $ very_active_distance      : num [1:862] 1.88 1.57 2.44 2.14 2.71 ...
 $ moderately_active_distance: num [1:862] 0.55 0.69 0.4 1.26 0.41 ...
 $ light_active_distance     : num [1:862] 6.06 4.71 3.91 2.83 5.04 ...
 $ sedentary_active_distance : num [1:862] 0 0 0 0 0 0 0 0 0 0 ...
 $ very_active_minutes       : num [1:862] 25 21 30 29 36 38 42 50 28 19 ...
 $ fairly_active_minutes     : num [1:862] 13 19 11 34 10 20 16 31 12 8 ...
 $ lightly_active_minutes    : num [1:862] 328 217 181 209 221 164 233 264 205 211 ...
 $ sedentary_minutes         : num [1:862] 728 776 1218 726 773 ...
 $ calories                  : num [1:862] 1985 1797 1776 1745 1863 ...

Univariate analysis for daily_activity_clean

Numerical variables

# Subset numeric columns 
num_df <- select_if(daily_activity_clean, is.numeric)

# Identify numeric columns
colnames(num_df)
 [1] "total_steps"                "total_distance"             "tracker_distance"           "logged_activities_distance"
 [5] "very_active_distance"       "moderately_active_distance" "light_active_distance"      "sedentary_active_distance" 
 [9] "very_active_minutes"        "fairly_active_minutes"      "lightly_active_minutes"     "sedentary_minutes"         
[13] "calories"                  

# plotting all numerical variables
col_names <- colnames(num_df)
for (i in col_names) {
  suppressWarnings(print(
    ggplot(num_df, aes(num_df[[i]])) +
      geom_histogram(
        bins = 30,
        color = "black",
        fill = "gray",
        aes(y = ..density..)
      ) +
      geom_density(
        color = "blue",
        size = 1
      ) +
      xlab(i) + ylab("Count") +
      ggtitle(paste("Histogram and Density Plot of", i))
  ))
}

NA
NA
NA
NA

Observations:

  • Many variables show a right-skewed distribution: a larger number of data values are located on the left side of the curve

  • The variables total_steps, total_distance, tracker_distance have a similar distribution. We can explore their correlations later

  • Since the distributions are not normal. The median is a better indicator of central tendency for the numerical variables in these data set

  • The variable logged_activities_distance and sedentary_active_distance might not provide useful information since most of the data points are zero. It seems that the users are not logging the distance frequently

  • The following variables seem related. We will explore them further in the bivariate analysis section:

sedentary_minutes; sedentary_active_distance lightly_active_minutes; light_active_distance
fairly_active_minutes; moderately_active_distance very_active_minutes; very_active_distance

  • The variables calories and sedentary_minutes exhibit a multimodal distribution, indicating the presence of subpopulations within the data. In this dataset, gender could be a potential variable that would result in a bimodal distribution when examining histograms of calories and sedentary minutes. Unfortunately, the gender of the users is not provided, limiting our ability to confirm this hypothesis.

Categorical variables

# Subset numeric columns 

select_if(daily_activity_clean, negate(is.numeric))
NA
# Check counts by id
ggplot(data=daily_activity_clean) + 
  geom_bar(mapping = aes (x= reorder(id, id,length)))+
  xlab("id") +
  coord_flip()

  
#https://stackoverflow.com/a/9231857/15333580

#reorder(id, id, length) takes the id variable, uses itself to determine the order, and uses the length() function to calculate the values used for ordering. Essentially, this reorders the levels of the id variable based on the length of their names.
count_max_ratio <- daily_activity_clean %>%
  count(id) %>%
  rename(id = "id", count = "n") %>%
  mutate(percent_of_max = count / max(count) * 100) %>%
  arrange(desc(percent_of_max))

# Create bar graph with percentage of entries compared to maximum
ggplot(count_max_ratio, aes(x = reorder(id, percent_of_max), y = percent_of_max)) +
  geom_bar(stat = "identity") +
  xlab("ID") +
  ylab("Percentage of Maximum Count") +
  ggtitle("Count by ID and Percentage of Maximum Count") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5)) +
  geom_hline(yintercept=50, color="orange", linewidth=1)+
  geom_hline(yintercept=75, color="red", linewidth=1)+
  coord_flip()

NA
NA
# percent_of_max > 75%

percent_of_max_top_75 <- filter(count_max_ratio, percent_of_max >=75)
percent_of_max_top_75 
# percent_of_max < 75

percent_of_max_under_75 <- filter(count_max_ratio, percent_of_max < 75)
percent_of_max_under_75 
daily_activity_clean$activity_date %>% summary()
        Min.      1st Qu.       Median         Mean      3rd Qu.         Max. 
"2016-04-12" "2016-04-18" "2016-04-26" "2016-04-26" "2016-05-03" "2016-05-12" 
ggplot(data=daily_activity_clean , aes(x = activity_date)) + 
  geom_histogram(binwidth = 1, color = "black", fill = "lightblue") +
  labs(x = "Activity Date", y = "Frequency", title = "Distribution of Activity Date") 

Observations:

  • It appears that there is missing activity data towards the end of the available period, specifically in the beginning of May
# Investigate if the missing activity data coincides with the absence of entries for certain user IDs.

ggplot(data=subset(daily_activity_clean, id %in% percent_of_max_top_75$id), aes(x = activity_date)) + 
  geom_histogram(binwidth = 1, color = "black", fill = "lightblue") +
  labs(x = "Activity Date", y = "Frequency", title = "Distribution of Activity Date For IDs with Above 75% of Entries")


ggplot(data=subset(daily_activity_clean, id %in% percent_of_max_under_75$id), aes(x = activity_date)) + 
  geom_histogram(binwidth = 1, color = "black", fill = "lightblue") +
  labs(x = "Activity Date", y = "Frequency", title = "Distribution of Activity Date For IDs with under 75% of Entries")

  • Users with more than 75% of data consistently report activity dates, while those with less than 75% of data show a decline in reporting starting from the end of April. The decline in Activity Date seems to be primarily due to a lack of data reporting from some users during that period.

Bivariate analysis

Correlation between numerical variables

corr <- cor(select_if(daily_activity_clean, is.numeric))

ggcorrplot(corr,
           hc.order = TRUE,
           type = "lower",
           lab = TRUE,
           colors = c("firebrick", "white", "royalblue"),
           lab_size = 4,
           lab_col = "black",
           title = "Correlation Between Numerical Variables")


#https://rdrr.io/github/microresearcher/MicroVis/man/ggcorrplot.html

sedentary_minutes; sedentary_active_distance lightly_active_minutes; light_active_distance
fairly_active_minutes; moderately_active_distance very_active_minutes; very_active_distance

# Compute correlation matrix
corr_matrix <- corr

# Set the threshold for correlation
threshold <- 0.60

# Find pairs of highly correlated variables
high_cor_pairs <- which(abs(corr_matrix) > threshold & lower.tri(corr_matrix, diag = FALSE), arr.ind = TRUE)

# Extract the variable names and correlation coefficients for the correlated pairs
variable_names <- colnames(corr_matrix)
cor_values <- as.vector(corr_matrix[high_cor_pairs])

# Create a data frame to store the correlated pairs and their correlation coefficients
cor_data <- data.frame(
  Variable1 = variable_names[high_cor_pairs[, 1]],
  Variable2 = variable_names[high_cor_pairs[, 2]],
  Correlation = cor_values
)

# Sort the correlated pairs by correlation coefficient in descending order
sorted_cor_data <- cor_data[order(-cor_data$Correlation), ]

# Remove the index
row.names(sorted_cor_data) <- NULL

# Display the sorted correlated variable pairs in the dataframe
print(sorted_cor_data)
NA
  • Total_distance, tracker_distance, and total steps are highly correlated, so we will retain only total distance and total steps as they provide similar information.

  • The following minute and distance types are correlated. Which indicates that they report different aspects of the same activity, this is time or distance:

    • lightly_active_minutes and light_active_distance (corr = 0.85)
    • fairly_active_minutes and moderately_active_distance (corr = 0.94)
    • very_active_minutes and very_active_distance (corr = 0.82)
  • There is a moderately high correlation between the time spent during very active periods and the total number of steps/total distance:

    • The correlation between very_active_minutes and total_distance is 0.68
    • The correlation between very_active_minutes and total_steps is 0.66
  • There is a moderate correlation of 0.61 between the total duration of very active minutes and the estimated daily calories consumed.

  • There is a moderate correlation of 0.62 between the total distance covered and the estimated daily calories consumed.

  • There is a moderate correlation coefficient of 0.60 between the distance covered during light activity (light_active_distance) and the total number of steps taken (total_steps).

Scatterplots of selected highly correlated variables pairs (>0.60)


# List of correlated variable pairs
correlated_pairs <- list(c("total_steps", "total_distance"), 
                         c("lightly_active_minutes", "light_active_distance"),
                         c("fairly_active_minutes", "moderately_active_distance"),
                         c("very_active_minutes", "very_active_distance"),
                         c("very_active_minutes", "total_distance"),
                         c("very_active_minutes", "total_steps"),
                         c("very_active_minutes", "calories"),
                         c("total_distance", "calories"),
                         c("light_active_distance", "total_steps"))

# Loop over each pair and create scatter plot
for (pair in correlated_pairs) {
  var1 <- pair[1]
  var2 <- pair[2]
  
  # Calculate averages
  avg_var1 <- mean(daily_activity_clean[[var1]], na.rm = TRUE)
  avg_var2 <- mean(daily_activity_clean[[var2]], na.rm = TRUE)
  
  # Create scatter plot using ggplot2 with aes()
  print(ggplot(data = daily_activity_clean, aes(x = !!sym(var1), y = !!sym(var2))) +
    geom_point() +
    geom_vline(xintercept = avg_var1, linetype = "dashed", color = "red") +
    geom_hline(yintercept = avg_var2, linetype = "dashed", color = "blue") +
    xlab(var1) + ylab(var2) +
    ggtitle(paste("Scatter Plot with Average Reference Lines of", var1, "vs", var2)))
}

User Behavior for daily activity dataset

Total steps: Total number of steps taken.

# Create a boxplot for total_steps
boxplot(daily_activity_clean$total_steps, 
        main = "Boxplot of Total Steps",
        ylab = "Total Steps")

# Calculate the median and standard deviation
median_value <- median(daily_activity_clean$total_steps)
std_dev <- round(sd(daily_activity_clean$total_steps),2)

# Identify outliers
outliers <- boxplot.stats(daily_activity_clean$total_steps)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", median_value, 
                      "\nStandard Deviation:", std_dev, 
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.85)


# Steps averages by IDs
steps_df <- daily_activity_clean %>%
  group_by(id) %>%
  summarise(average_steps = mean(total_steps), median_steps =median(total_steps), n = n())

steps_df
# Calculate percentages for the average column
at_least_10k_avg <- sum(steps_df$average_steps >= 10000) / nrow(steps_df) * 100
between_5K_10K_avg <- sum(steps_df$average_steps >= 5000 & steps_df$average_steps < 10000) / nrow(steps_df) * 100
below_5k_avg <- sum(steps_df$average_steps < 5000) / nrow(steps_df) * 100

# Calculate percentages for the median column
at_least_10k_med <- sum(steps_df$median_steps >= 10000) / nrow(steps_df) * 100
between_5K_10K_med <- sum(steps_df$median_steps >= 5000 & steps_df$median_steps < 10000) / nrow(steps_df) * 100
below_5k_med <- sum(steps_df$median_steps < 5000) / nrow(steps_df) * 100

# Create a data frame for the steps categories
percentage_steps_df<- data.frame(
  Category = c("Below 5,000", "Between 5,000 and 10,000", "At least 10,000"),
  Percentage_Average = round(c(below_5k_avg, between_5K_10K_avg, at_least_10k_avg)),
  Percentage_Median = round(c(below_5k_med, between_5K_10K_med, at_least_10k_med)))
percentage_steps_df
NA
# Convert Category to a factor with custom factor levels
percentage_steps_df$Category <- factor(percentage_steps_df$Category, levels = c("Below 5,000", "Between 5,000 and 10,000", "At least 10,000"))

# Create a bar plot using ggplot
ggplot(percentage_steps_df, aes(x = Category, y = Percentage_Average)) +
  geom_bar(stat = "identity", fill = "blue") +
  labs(x = "Average Total Steps", y = "Percentage of Users", title = "58% of Users Average 5,000-10,000 Step Daily",subtitle = "Only 21% Achieve the 10,000-Step Goal") +
  geom_text(aes(label = paste0(Percentage_Average, "%")), vjust = -0.5, color = "black") + 
  ylim(0, 100) +  theme_minimal() + theme(panel.grid = element_blank())

NA
NA

Total Distance: Total kilometers tracked.

# Create a boxplot for total_distance
boxplot(daily_activity_clean$total_distance, 
        main = "Boxplot of Total Distance",
        ylab = "Total Distance")

# Calculate the median and standard deviation
median_value <- median(daily_activity_clean$total_distance)
std_dev <- sd(daily_activity_clean$total_distance)

# Identify outliers
outliers <- boxplot.stats(daily_activity_clean$total_distance)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", round(median_value, 2), 
                      "\nStandard Deviation:", round(std_dev, 2), 
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n")


# Total distance by IDs
t_distance_df <- daily_activity_clean %>%
  group_by(id) %>%
  summarise(average_t_distance = mean(total_distance ), median_t_distance =median(total_distance), n = n())

t_distance_df
# Calculate percentages for the average column
at_least_10_avg<- sum(t_distance_df$average_t_distance>= 10) / nrow(t_distance_df) * 100
between_5_10_avg <- sum(t_distance_df$average_t_distance >= 5 & t_distance_df$average_t_distance < 10) / nrow(t_distance_df) * 100
below_5_avg <- sum(t_distance_df$average_t_distance < 5) / nrow(t_distance_df) * 100


# Create a data frame for the distance categories
percentage_t_distance_df<- data.frame(
  Category = c("Below 5 km", "Between 5 and 10 km", "At least 10 km"),
  Percentage_Average = round(c(below_5_avg, between_5_10_avg , at_least_10_avg)))
percentage_t_distance_df



# Convert Category to a factor with custom factor levels
percentage_t_distance_df$Category <- factor(percentage_t_distance_df$Category, levels = c("Below 5 km", "Between 5 and 10 km", "At least 10 km"))
# Create a bar plot using ggplot
ggplot(percentage_t_distance_df, aes(x = Category, y = Percentage_Average)) +
  geom_bar(stat = "identity", fill = "pink") +
  labs(x = "Average Total Distance", y = "Percentage of Users", title = "55% of Users Average 5-10 Kilometers Daily",subtitle = "10,000 steps is approximately equal to covering 5 miles (or 8 kilometers)") +
  geom_text(aes(label = paste0(Percentage_Average, "%")), vjust = -0.5, color = "black") + 
  ylim(0, 100) +  theme_minimal() +theme(panel.grid = element_blank())

Sedentary Minutes: Total minutes spent in sedentary activity.

# Create a boxplot for sedentary_minutes
boxplot(daily_activity_clean$sedentary_minutes,
        main = "Boxplot of Sedentary Minutes",
        ylab = "Sedentary Minutes")

# Calculate the median and standard deviation
median_value <- median(daily_activity_clean$sedentary_minutes)
std_dev <- sd(daily_activity_clean$sedentary_minutes)

# Identify outliers
outliers <- boxplot.stats(daily_activity_clean$sedentary_minutes)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", round(median_value, 2),
                      "\nStandard Deviation:", round(std_dev, 2),
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.80)

NA
NA
  • These are high values for sedentary minutes. For instance, 1020 minutes is equivalent to 17 hours, and 1400 minutes is equivalent to 24 hours. After performing a quick search, it seems that the Fitbit uses 1400 as default for sedentary minutes when the device is not worn and it includes the sleeping time. SedentaryMinutes is total minutes spent in sedentary activity according to the data dictionary. See meta data section. Therefore, we need to substract the times sleeping to obtain an more accurate estimate of daily sedentary minutes.

Sleep time is not considered sedentary time, so it was removed to determine the waking day and to allow the proportion of the day spent sedentary to be calculated

# Check sedentary_minutes stats
daily_activity_clean$sedentary_minutes %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    0.0   721.2  1020.5   955.2  1189.0  1440.0 
outliers
[1]  2 13  0
# Count entries where sedentary minutes equal 1440
count_1440 <- sum(daily_activity_clean$sedentary_minutes == 1440)

# Output the count
count_1440
[1] 7
# Remove rows with sedentary minutes equal to the default value (1440) and outliers

daily_activity_clean <- filter(daily_activity_clean, !(sedentary_minutes %in% c(0, 2, 13, 1440)))


# Rename the column
daily_sleep_clean <- rename(daily_sleep_clean, activity_date = sleep_day)

# Join the datasets
joined_activity_sleep <- inner_join(daily_activity_clean, daily_sleep_clean, by = c("id", "activity_date"))


# Check missing values and duplicates
cat(
  "\n",
  "Missing values:",
  sum(is.na(joined_activity_sleep )),
  "\n",
  "Duplicate values:",
  sum(duplicated(joined_activity_sleep )),
  "\n",
  "Unique Ids:",
  n_distinct(joined_activity_sleep $id)
)

 Missing values: 0 
 Duplicate values: 0 
 Unique Ids: 24
# Create a derived column for sedentary minutes that does not include sleep time
joined_activity_sleep <- joined_activity_sleep %>%
  mutate(
    sedentary_min_awake = sedentary_minutes - total_minutes_asleep,
    sedentary_hours_awake = sedentary_min_awake / 60,
    sedentary_percentage_diff = (sedentary_minutes - sedentary_min_awake) / sedentary_minutes * 100
  )

# Let us check the percentage difference of sedentary_minutes and the new column "sedentary_min_awake

# Create a boxplot for sedentary_percentage_diff
boxplot(joined_activity_sleep$sedentary_percentage_diff,
        main = "Boxplot of Sedentary Percentage Difference",
        ylab = "Sedentary Percentage Difference")

# Calculate the median and standard deviation
median_value <- median(joined_activity_sleep$sedentary_percentage_diff)
std_dev <- sd(joined_activity_sleep$sedentary_percentage_diff)

# Identify outliers
outliers <- boxplot.stats(joined_activity_sleep$sedentary_percentage_diff)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", round(median_value, 2),
                      "\nStandard Deviation:", round(std_dev, 2),
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.80)

  • The sedentary percentage difference has a median value of 59.95%, indicating a significant distinction between sedentary_minutes and sedentary_min_awake. This suggest that the original column “sedentary_minutes” included the time asleep.
# Create a boxplot for sedentary_min_awake
boxplot(joined_activity_sleep$sedentary_min_awake,
        main = "Boxplot of Sedentary Minutes Awake",
        ylab = "Sedentary Minutes Awake")

# Calculate the median and standard deviation
median_value <- median(joined_activity_sleep$sedentary_min_awake)
std_dev <- sd(joined_activity_sleep$sedentary_min_awake)

# Identify outliers
outliers <- boxplot.stats(joined_activity_sleep$sedentary_min_awake)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", round(median_value, 2),
                      "\nStandard Deviation:", round(std_dev, 2),
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.80)

  • Observation: There appears to be an inconsistency in the data. The sedentary_minutes value is smaller than the total_minutes_asleep value, which is unexpected.
# Count the number of cases where sedentary_minutes is smaller than total_minutes_asleep
count <- sum(joined_activity_sleep$sedentary_minutes < joined_activity_sleep$total_minutes_asleep)

# Print the count
count
[1] 42
# Subset the dataset
subset_data <- joined_activity_sleep[joined_activity_sleep$sedentary_minutes < joined_activity_sleep$total_minutes_asleep, ]

# View the subsetted data
subset_data
NA
# Check column names of the subsetted data
subset_data %>%
select(sedentary_minutes, total_minutes_asleep, sedentary_min_awake, calories,id, activity_date, total_steps, total_distance, very_active_minutes )
dim(subset_data)
[1] 42 21
dim(joined_activity_sleep)
[1] 408  21
# Use anti_join() to return a new dataset that includes all rows from the first dataset except for the rows that have a match in the second dataset.
 clean_subset<- anti_join(joined_activity_sleep, subset_data)
Joining with `by = join_by(id, activity_date, total_steps, total_distance, tracker_distance, logged_activities_distance, very_active_distance, moderately_active_distance, light_active_distance, sedentary_active_distance, very_active_minutes, fairly_active_minutes, lightly_active_minutes, sedentary_minutes, calories, total_sleep_records, total_minutes_asleep, total_time_in_bed, sedentary_min_awake, sedentary_hours_awake, sedentary_percentage_diff)`
dim(clean_subset)
[1] 366  21
# Create a boxplot for sedentary_min_awake
boxplot(clean_subset$sedentary_min_awake,
        main = "Boxplot of Sedentary Minutes Awake",
        ylab = "Sedentary Minutes Awake")

# Calculate the median and standard deviation
median_value <- median(clean_subset$sedentary_min_awake)
std_dev <- sd(clean_subset$sedentary_min_awake)

# Identify outliers
outliers <- boxplot.stats(clean_subset$sedentary_min_awake)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", round(median_value, 2),
                      "\nStandard Deviation:", round(std_dev, 2),
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.80)

Observation: By eliminating negative values from “sedentary_min_awake,” the resulting values now reflect a more realistic scenario.

# Total sedentary minutes awake by IDs
t_sedentary_df <- clean_subset %>%
  group_by(id) %>%
  summarise(average_sedentary_min_awake = mean(sedentary_min_awake),
            median_sedentary_min_awake = median(sedentary_min_awake), n = n())

t_sedentary_df
NA
dataset <- t_sedentary_df
column <- "average_sedentary_min_awake"
new_categories <- c("Below 200 minutes", "Between 200 and 400 minutes", "At least 400 minutes")

# Calculate percentages for the average column
below_200_avg <- sum(dataset[[column]] < 200) / nrow(dataset) * 100
between_200_400_avg <- sum(dataset[[column]] >= 200 & dataset[[column]] <= 400) / nrow(dataset) * 100
at_least_400_avg <- sum(dataset[[column]] >= 400) / nrow(dataset) * 100

# Create a data frame for the categories
percentage_sedentary_awake_df <- data.frame(
  Category = new_categories,
  Percentage_Average = round(c(below_200_avg, between_200_400_avg, at_least_400_avg))
)

# Convert Category to a factor with custom factor levels
percentage_sedentary_awake_df$Category <- factor(percentage_sedentary_awake_df$Category, levels = new_categories)

percentage_sedentary_awake_df
NA
NA
NA
# Create a bar plot using ggplot
ggplot(percentage_sedentary_awake_df, aes(x = Category, y = Percentage_Average)) +
  geom_bar(stat = "identity", fill = "gray") +
  labs(x = "Average Total Sedentary Min Awake", y = "Percentage of Users", 
       title = "48% of Users Have an Average of at Least 400 Daily Sedentary Minutes While Awake",
       subtitle = "200 Minutes are 3 hours and 20 minutes; 400 min are 6 hours and 40 min") +
  geom_text(aes(label = paste0(Percentage_Average, "%")), vjust = -0.5, color = "black") + 
  ylim(0, 100) +
  theme_minimal() +
  theme(panel.grid = element_blank(), plot.title = element_text(size = 12), plot.subtitle = element_text(size = 10))

In a representative sample of U.S. adults, over two-thirds spent 6 + hours/day sitting, and more than half did not meet the recommended 150 min/week of physical activity. The study discovered that prolonged sitting for 6+ hours/day was associated with higher body fat percentages. While exceeding 150 min/week of physical activity was linked to lower body fat percentages, achieving recommended activity levels may not fully offset the increased body fat from prolonged sitting.

Jingwen Liao, Min Hu, Kellie Imm, Clifton J. Holmes, Jie Zhu, Chao Cao, Lin Yang. Association of daily sitting time and leisure-time physical activity with body fat among U.S. adults. Journal of Sport and Health Science, 2022. ISSN 2095-2546. https://doi.org/10.1016/j.jshs.2022.10.001. (https://www.sciencedirect.com/science/article/pii/S2095254622001016)

Calories:Total estimated energy expenditure (in kilocalories).

# Create a boxplot for calories
boxplot(daily_activity_clean$calories, 
        main = "Boxplot of Calories",
        ylab = "Calories")

# Calculate the median and standard deviation
median_value <- median(daily_activity_clean$calories)
std_dev <- round(sd(daily_activity_clean$calories),2)

# Identify outliers
outliers <- boxplot.stats(daily_activity_clean$calories)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", median_value, 
                      "\nStandard Deviation:", std_dev, 
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.85)

outliers
[1] 4552 4392 4501 4546 4900 4547 4398
# Calories averages by IDs
calories_df <- daily_activity_clean %>%
  group_by(id) %>%
  summarise(average_calories = mean(calories), median_calories = median(calories))

calories_df
NA
# Calculate percentages for the average column
below_1600_avg <- sum(calories_df$average_calories < 1600) / nrow(calories_df) * 100
between_1600_2200_avg <- sum(calories_df$average_calories >= 1600 & calories_df$average_calories < 2200) / nrow(calories_df) * 100
between_2200_3000_avg <- sum(calories_df$average_calories >= 2200 & calories_df$average_calories < 3000) / nrow(calories_df) * 100
at_least_3000_avg <- sum(calories_df$average_calories >= 3000) / nrow(calories_df) * 100

# Calculate percentages for the median column
below_1600_med <- sum(calories_df$median_calories < 1600) / nrow(calories_df) * 100
between_1600_2200_med <- sum(calories_df$median_calories >= 1600 & calories_df$median_calories < 2200) / nrow(calories_df) * 100
between_2200_3000_med <- sum(calories_df$median_calories >= 2200 & calories_df$median_calories < 3000) / nrow(calories_df) * 100
at_least_3000_med <- sum(calories_df$median_calories >= 3000) / nrow(calories_df) * 100

# Create a data frame for the calories categories
percentage_calories_df <- data.frame(
  Category = c("Below 1,600", "Between 1,600 and 2,200", "Between 2,200 and 3,000", "At least 3,000"),
  Percentage_Average = round(c(below_1600_avg, between_1600_2200_avg, between_2200_3000_avg, at_least_3000_avg)),
  Percentage_Median = round(c(below_1600_med, between_1600_2200_med, between_2200_3000_med, at_least_3000_med))
)

# Convert Category to a factor with custom factor levels
percentage_calories_df$Category <- factor(percentage_calories_df$Category, levels = c("Below 1,600", "Between 1,600 and 2,200", "Between 2,200 and 3,000", "At least 3,000"))

percentage_calories_df
NA
# Create a bar plot using ggplot
ggplot(percentage_calories_df, aes(x = Category, y = Percentage_Average)) +
  geom_bar(stat = "identity", fill = "red") +
  labs(x = "Calorie Categories", y = "Percentage of Users", 
       title = "42% of Users Have an Average Daily Calorie Expenditure Between 1,600 and 2,200.",
       subtitle = "Most females require 1,600 to 2,200 calories per day, as per the Dietary Guidelines for Americans, 2020-2025") +
  geom_text(aes(label = paste0(Percentage_Average, "%")), vjust = -0.5, color = "black") + 
  ylim(0, 100) +
  theme_minimal() +
  theme(panel.grid = element_blank(), 
        plot.title = element_text(size = 12), 
        plot.subtitle = element_text(size = 10))

“Females ages 19 through 30 require about 1,800 to 2,400 calories a day. Males in this age group have higher calorie needs of about 2,400 to 3,000 a day. Calorie needs for adults ages 31 through 59 are generally lower; most females require about 1,600 to 2,200 calories a day and males require about 2,200 to 3,000 calories a day.”

U.S. Department of Agriculture and U.S. Department of Health and Human Services. Dietary Guidelines for Americans, 2020-2025. 9th Edition. December 2020. Available at DietaryGuidelines.gov/

Intensity Minutes: Time spent in one of four intensity categories.

  • VeryActiveMinutes: Total minutes spent in very active activity

  • FairlyActiveMinutes: Total minutes spent in moderate activity

  • LightlyActiveMinutes: Total minutes spent in light activity

  • SedentaryMinutes: Total minutes spent in sedentary activity

activity_minutes_df <- daily_activity_clean %>%
  group_by(id) %>%
  summarise(
    average_very_active_minutes = mean(very_active_minutes),
    average_fairly_active_minutes = mean(fairly_active_minutes),
    average_lightly_active_minutes = mean(lightly_active_minutes),
    average_sedentary_minutes = mean(sedentary_minutes)
  )

activity_minutes_df
NA
NA

# Define the custom order of legend items
custom_order <- c( "Very Active", "Fairly Active", "Lightly Active", "Sedentary")

# Create the stacked bar plot
ggplot(activity_minutes_df, aes(y = id)) +
  geom_bar(aes(x = average_sedentary_minutes, fill = "Sedentary"), stat = "identity", width = 0.5) +
  geom_bar(aes(x = average_lightly_active_minutes, fill = "Lightly Active"), stat = "identity", width = 0.5) +
  geom_bar(aes(x = average_fairly_active_minutes, fill = "Fairly Active"), stat = "identity", width = 0.5) +
  geom_bar(aes(x = average_very_active_minutes, fill = "Very Active"), stat = "identity", width = 0.5) +
  xlab("Minutes") +
  ylab("ID") +
  ggtitle("Average Activity Minutes by ID") +
  scale_fill_manual(name = "", values = c("Very Active" = "red", "Fairly Active" = "orange", "Lightly Active" = "lightgreen", "Sedentary" = "lightblue"), breaks = custom_order) +
  theme_minimal() +
  theme(legend.position = "bottom", panel.grid = element_blank()) 

NA
NA
NA
NA
NA
NA
NA
NA
# Calculate the average for each column
averages <- colMeans(activity_minutes_df[, c("average_very_active_minutes",
                                             "average_fairly_active_minutes",
                                             "average_lightly_active_minutes",
                                             "average_sedentary_minutes")])

# Calculate the total average
total_average <- sum(averages)

# Calculate the proportions
proportions <- averages / total_average

# Create the new dataframe with modified row names
overall_average_df<- data.frame(Average = averages,
                     Percentage = proportions * 100)

# Modify the row names
row_names <- c("Very Active", "Fairly Active", "Lightly Active", "Sedentary")
row.names(overall_average_df) <- row_names

# Print the new dataframe
overall_average_df
NA
NA

ggplot(overall_average_df, aes(x = Percentage, y = reorder(row.names(overall_average_df), Percentage), fill = row.names(overall_average_df))) +
  geom_bar(stat = "identity", width = 0.7, show.legend = FALSE) +
  geom_text(aes(label = paste0(round(Percentage), "%")), hjust = -0.2, color = "black", size = 4) +
  ylab("Minutes Intensity") +
  xlab("Percentage") +
  ggtitle("Users' Overall Average Intensity Minutes Consist Primarily of Sedentary and Lightly Active Time") +
  scale_fill_manual(values = c("Very Active" = "red", "Fairly Active" = "orange", "Lightly Active" = "lightgreen", "Sedentary" = "lightblue")) +
 scale_x_continuous(labels = NULL) +
  theme_minimal() +
  theme(legend.position = "none", panel.grid = element_blank(), axis.text.y = element_text(size = 10))

NA
NA

“Analyzing each individual’s average calorie intake can provide insights into their individual dietary habits and patterns. By comparing the individual averages to the overall average, you can identify individuals who consume more or fewer calories compared to the group average. This comparison can help in understanding variations in calorie intake and potential factors influencing individual differences.”

# Define the custom order of legend items

custom_order <- c("Very Active", "Fairly Active", "Lightly Active", "Sedentary")


# Create the stacked horizontal bar chart
ggplot(overall_average_df, aes(x = Percentage, y = factor(1), fill = factor(row.names(overall_average_df), levels = custom_order))) +
  geom_bar(stat = "identity", width = 0.7) +
  xlab("Percentage") +
  ylab("") +
  ggtitle("Users' Overall Average Intensity Minutes Consist Primarily of Sedentary and Lightly Active Time") +
  scale_fill_manual(
    name = "",
    values = c(
      "Very Active" = "red",
      "Fairly Active" = "orange",
      "Lightly Active" = "lightgreen",
      "Sedentary" = "lightblue"
    ),
    breaks = custom_order
  ) +
  guides(fill = guide_legend(reverse = TRUE)) +  # Reverse the order of the legend
  theme_minimal() +
  theme(legend.position = "top", 
        panel.grid = element_blank(),
         axis.text.y = element_blank(),  # Remove the y-axis text
         plot.title = element_text(size = 12, margin = margin(b = 20))) + # Adjust the title size and margin
  geom_vline(xintercept = 97, color = "black", linetype = "dashed") +
  annotate("text", x = 97, y = 1, label = "   97%", vjust = -5.5, hjust = 0.1)

NA
NA

These indicators provide insights into activity levels, sedentary behavior, and calorie burn. They can help track progress, set goals, and evaluate user behavior over time. Remember to consider the specific context and goals of your analysis to select and customize the most relevant KPIs for your use case. The context I will use is the guidelines for physical activity and diet for Americans:

EDA for daily_sleep_clean

str(daily_sleep_clean)
tibble [410 × 5] (S3: tbl_df/tbl/data.frame)
 $ id                  : chr [1:410] "1503960366" "1503960366" "1503960366" "1503960366" ...
 $ activity_date       : Date[1:410], format: "2016-04-12" "2016-04-13" "2016-04-15" "2016-04-16" ...
 $ total_sleep_records : num [1:410] 1 2 1 2 1 1 1 1 1 1 ...
 $ total_minutes_asleep: num [1:410] 327 384 412 340 700 304 360 325 361 430 ...
 $ total_time_in_bed   : num [1:410] 346 407 442 367 712 320 377 364 384 449 ...
  • activity_date (sleep_day): Date on which the sleep event started.
  • total_sleep_records: Number of recorded sleep periods for that day. Includes naps > 60 min.
  • total_minutes_asleep: Total number of minutes classified as being “asleep”.
  • total_time_in_bed: Total minutes spent in bed, including asleep, restless, and awake, that occurred during a defined sleep record.
#Sanity check: Verify that the value of total_time_in_bed is greater than total_minutes_asleep, as we would expect.
daily_sleep_clean[daily_sleep_clean$total_time_in_bed < daily_sleep_clean$total_minutes_asleep,]

Univariate analysis


numerical_cols <- daily_sleep_clean%>%
  select_if(is.numeric)

# plotting all numerical variables
col_names <- colnames(numerical_cols )
for (i in col_names) {
  suppressWarnings(print(
    ggplot(numerical_cols , aes(numerical_cols [[i]])) +
      geom_histogram(
        bins = 30,
        color = "black",
        fill = "gray",
        aes(y = ..density..)
      ) +
      geom_density(
        color = "blue",
        size = 1
      ) +
      xlab(i) + ylab("Count") +
      ggtitle(paste("Histogram with Density Plot of", i))
  ))
}

Bivariate analysis

Correlation between numerical variables

# Correlation between numerical variables
corr <- cor(select_if(daily_sleep_clean, is.numeric))

ggcorrplot(corr,
           hc.order = TRUE,
           type = "lower",
           lab = TRUE,
           colors = c("firebrick", "white", "royalblue"),
           lab_size = 4,
           lab_col = "black",
           title = "Correlation Between Numerical Variables")

NA
NA

Scatterplots of total_minutes_asleep vs total_time_in_bed


ggplot(data = daily_sleep_clean, aes(x = total_minutes_asleep, y = total_time_in_bed)) +
  geom_point()

User Behavior for daily sleep dataset



frequency_table <- as.data.frame(table(daily_sleep_clean$total_sleep_records))
frequency_table$Percentage <- frequency_table$Freq / sum(frequency_table$Freq) * 100

ggplot(data = frequency_table, aes(x = Var1, y = Freq)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  geom_text(aes(label = paste(Freq, " (", percent(Percentage / 100), ")", sep = "")),
            hjust = 0.5,  vjust = -0.4, color = "black") +
  labs(x = "Total Sleep Records", y = "Frequency",
       title = "Uncommon Napping: 89% of Sleep Records Indicate a Singular Sleep Period.",
       subtitle = "Includes naps > 60 min.")+
  theme_minimal() +
  theme(panel.grid = element_blank(),
        plot.title = element_text(size = 12),
        plot.subtitle = element_text(size = 10, margin = margin(b = 20)))

NA
NA
NA

Total Minutes Asleep

# Create a boxplot for total_minutes_asleep
boxplot(daily_sleep_clean$total_minutes_asleep, 
        main = "Boxplot of Total Minutes Asleep",
        ylab = "Total Minutes Asleep")

# Calculate the median and standard deviation
median_value <- median(daily_sleep_clean$total_minutes_asleep)
std_dev <- round(sd(daily_sleep_clean$total_minutes_asleep), 2)

# Identify outliers
outliers <- boxplot.stats(daily_sleep_clean$total_minutes_asleep)$out

# Count the number of outliers
num_outliers <- length(outliers)

# Create the legend label with median, standard deviation, and outlier count
legend_label <- paste("Median:", median_value, 
                      "\nStandard Deviation:", std_dev, 
                      "\nOutliers:", num_outliers)

# Add the legend with median, standard deviation, and outlier count
legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.85)

# Sleep duration averages by IDs with standard deviation and count (n)
sleep_df <- daily_sleep_clean %>%
  group_by(id) %>%
  summarise(average_sleep_minutes = mean(total_minutes_asleep),
            standard_deviation_sleep_minutes = sd(total_minutes_asleep),
            n = n())

sleep_df
NA
NA
NA
# Drop ID "2320127002" due to insufficient data for computing mean and standard deviation.
sleep_df <- sleep_df %>% 
filter(id != "2320127002")
sleep_df 
# Calculate percentages for the average column
below_6_hours <- sum(sleep_df$average_sleep_minutes < 360) / nrow(sleep_df) * 100
between_6_7_hours <- sum(sleep_df$average_sleep_minutes >= 360 & sleep_df$average_sleep_minutes < 420) / nrow(sleep_df) * 100
at_least_7_hours <- sum(sleep_df$average_sleep_minutes >= 420) / nrow(sleep_df) * 100

# Create a data frame for the sleep duration categories
percentage_sleep_df <- data.frame(
  Category = c("Below 6 hours", "Between 6 and 7 hours", "At least 7 hours"),
  Percentage_Average = round(c(below_6_hours, between_6_7_hours, at_least_7_hours))
)

# Convert Category to a factor with custom factor levels
percentage_sleep_df$Category <- factor(percentage_sleep_df$Category, levels = c("Below 6 hours", "Between 6 and 7 hours", "At least 7 hours"))

percentage_sleep_df
str(percentage_sleep_df)
'data.frame':   3 obs. of  2 variables:
 $ Category          : Factor w/ 3 levels "Below 6 hours",..: 1 2 3
 $ Percentage_Average: num  30 22 48


ggplot(percentage_sleep_df, aes(x = Category, y = Percentage_Average)) +
  geom_bar(stat = "identity", fill = "purple") +
  labs(x = "Average Sleep Duration", y = "Percentage of Users", 
       title = "52% of Users Get Less Than 7 Hours of Sleep on Average Daily") +
  geom_text(aes(label = paste0(Percentage_Average, "%")), vjust = -0.5, color = "black") + 
  ylim(0, 100) +
  theme_minimal() +
  theme(panel.grid = element_blank(), plot.title = element_text(size = 12), plot.subtitle = element_text(size = 10))

Sleep Duration Consistency

#Error bars

# Convert average_sleep_minutes and standard_deviation_sleep_minutes to hours
sleep_df$average_sleep_hours <- sleep_df$average_sleep_minutes / 60
sleep_df$standard_deviation_sleep_hours <- sleep_df$standard_deviation_sleep_minutes / 60


# Create a bar plot for each 'id' with error bars representing standard deviation
ggplot(sleep_df, aes(x = id, y = average_sleep_hours)) +
  geom_bar(stat = "identity", fill = "skyblue", color = "black") +
  geom_errorbar(aes(ymin = average_sleep_hours - standard_deviation_sleep_minutes / 60,
                    ymax = average_sleep_hours + standard_deviation_sleep_minutes / 60),
                width = 0.2, position = position_dodge(0.9), color = "black") +
  labs(x = "ID", y = "Average Sleep Duration (hours)",
       title = "Sleep Consistency: Average Sleep Duration with Error Bars",
       subtitle = "Error bars represent the standard deviation around the mean.") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  geom_hline(yintercept = 7, linetype = "dashed", color = "red") +
  scale_y_continuous(breaks = seq(0, 12, 1))  # Adjust the range as needed

# Calculate sleep duration averages and standard deviations in hours
sleep_df <- daily_sleep_clean %>%
  group_by(id) %>%
  summarise(n = n(),
            average_sleep_hours = mean(total_minutes_asleep) / 60,       # Convert minutes to hours
            average_time_in_bed_hours = mean(total_time_in_bed) / 60,   
            standard_deviation_sleep_hours = sd(total_minutes_asleep) / 60,     
            standard_deviation_time_in_bed_hours = sd(total_time_in_bed) / 60,    
           ) %>%
  mutate(time_difference_hours = average_time_in_bed_hours - average_sleep_hours,  # Calculate the time difference in hours
         average_awake_in_bed_hours = time_difference_hours,  # Rename column "awake_in_bed"
         sd_awake_in_bed_hours = sd(time_difference_hours))  # Calculate SD for "awake_in_bed" in hours


sleep_df
NA
NA
NA
# Drop ID "2320127002" due to insufficient data for computing mean and standard deviation.
sleep_df <- sleep_df %>% 
filter(id != "2320127002")
dim(sleep_df)
[1] 23  9
create_boxplots_in_one_output <- function(data_frame, columns_to_analyze, decimal_places = 2) {
  num_columns <- length(columns_to_analyze)
  num_rows <- ceiling(num_columns / 2)
  
  par(mfrow = c(num_rows, 2))  # Set the plotting layout
  
  for (i in 1:num_columns) {
    column_name <- columns_to_analyze[i]
    boxplot(data_frame[[column_name]],
            ylab = column_name)
    
    median_value <- median(data_frame[[column_name]])
    std_dev <- round(sd(data_frame[[column_name]]), decimal_places)
    outliers <- boxplot.stats(data_frame[[column_name]])$out
    num_outliers <- length(outliers)
    
    legend_label <- paste("Median:", round(median_value, decimal_places),
                          "\nSD:", std_dev,
                          "\nOutliers:", num_outliers)
    
    legend("topright", legend = legend_label, pch = "", col = "black", bty = "n", cex = 0.75)
  }
  
  par(mfrow = c(1, 1))  # Reset the plotting layout to default
}

# Columns to analyze
columns_to_analyze <- c("average_sleep_hours", "average_awake_in_bed_hours")

# Call the function to create boxplots in one output
create_boxplots_in_one_output(sleep_df, columns_to_analyze, decimal_places = 2)

# Columns to analyze
columns_to_analyze <- c("standard_deviation_sleep_hours", "sd_awake_in_bed_hours")

# Call the function
create_boxplots_in_one_output(sleep_df, columns_to_analyze, decimal_places = 2)

#Columns with outliers to remove
columns_with_outliers <- c("average_sleep_hours", "average_awake_in_bed_hours", "standard_deviation_sleep_hours")

# Function to remove outliers from a column
remove_outliers <- function(data, column_name) {
  outlier_bounds <- boxplot.stats(data[[column_name]])$out
  data_no_outliers<- data[!(data[[column_name]] %in% outlier_bounds), ]
  return(data_no_outliers)
}

# Loop through each column and remove outliers
for (col in columns_with_outliers) {
  sleep_df <- remove_outliers(sleep_df, col)
}
sleep_df
# Check if outliers were removed
columns_to_analyze <- c("average_sleep_hours", "average_awake_in_bed_hours", "standard_deviation_sleep_hours")


# Call the function to create boxplots in one output
create_boxplots_in_one_output(sleep_df, columns_to_analyze, decimal_places = 2)

#Let us divide the users into irregular sleepers and regular sleepers. We will use the 75th percentile as the threshold to determine irregular sleepers. The rest will be considered regular sleepers.

# Define the Threshold (e.g., using the 75th percentile)
threshold <- quantile(sleep_df$standard_deviation_sleep_hours, 0.75)

# Create a new column "sleeper_type" based on the threshold
sleep_df$sleeper_type <- ifelse(sleep_df$standard_deviation_sleep_hours > threshold, "irregular", "regular")
sleep_df
# sleep_type counts
table(sleep_df$sleeper_type)

irregular   regular 
        4        13 
sleep_df

color_options <- c("#E69F00", "#0072B2") # Blue: "#0072B2", Orange: "#E69F00"

# Function to create the violin plot for a given y-axis column
create_violin_plot <- function(data, x_axis_col, y_axis_col) {
  ggplot(data, aes_string(x = x_axis_col, y = y_axis_col, fill = x_axis_col)) +
    geom_violin(scale = "width", draw_quantiles = c(0.25, 0.5, 0.75), trim = FALSE) +
    geom_boxplot(width = 0.1, fill = "white", color = "black") +
    labs(x = "Sleeper Type", y = y_axis_col, title = paste("Comparison",x_axis_col,"for", y_axis_col)) +
    scale_fill_manual(values = color_options) +
    theme_minimal()
}
# Call the function to create the violin plots for each column
for (col in c("average_sleep_hours", "standard_deviation_sleep_hours", "average_awake_in_bed_hours","sd_awake_in_bed_hours")) {
  plot <- create_violin_plot(sleep_df, "sleeper_type", col)
  print(plot)
}

Observations:

  • Regular sleepers tend to have higher median average sleep hours compared to irregular sleepers.This suggests that individuals classified as regular sleepers are likely getting more sleep on average than those categorized as irregular sleepers.

  • Additionally, the spread of the “average_sleep_hours” for irregular sleepers appears to be wider, indicating more variability in their sleep duration. In contrast, the violin plot for regular sleepers shows a narrower spread, suggesting that their sleep duration is more consistent.

  • Regular sleepers exhibit a slightly higher median average awake-in-bed duration compared to irregular sleepers.

Summary: Regular sleepers get more sleep on average, have a more consistent sleep duration, and slightly higher median awake-in-bed duration than irregular sleepers.

EDA minute_sleep_clean

str(minute_sleep_clean)
tibble [187,978 × 4] (S3: tbl_df/tbl/data.frame)
 $ id    : chr [1:187978] "1503960366" "1503960366" "1503960366" "1503960366" ...
 $ date  : POSIXct[1:187978], format: "2016-04-12 02:47:30" "2016-04-12 02:48:30" "2016-04-12 02:49:30" "2016-04-12 02:50:30" ...
 $ value : Factor w/ 3 levels "1","2","3": 3 2 1 1 1 1 1 2 2 2 ...
 $ log_id: num [1:187978] 1.14e+10 1.14e+10 1.14e+10 1.14e+10 1.14e+10 ...

This data seems to come from the Classic Sleep Log (1 minute)

Value indicating the sleep state. 1 = asleep, 2 = restless, 3 = awake

For more detail check : Fitbit data dictionary

# Add labels to the velue column
minute_sleep_clean$value <- factor(minute_sleep_clean$value, levels = c("1", "2", "3"), labels = c("asleep", "restless", "awake"))
minute_sleep_clean %>% summary()
      id                 date                             value            log_id         
 Length:187978      Min.   :2016-04-11 20:48:00.00   asleep  :171960   Min.   :1.137e+10  
 Class :character   1st Qu.:2016-04-19 02:48:00.00   restless: 14002   1st Qu.:1.144e+10  
 Mode  :character   Median :2016-04-26 21:48:00.00   awake   :  2016   Median :1.150e+10  
                    Mean   :2016-04-26 13:31:23.11                     Mean   :1.150e+10  
                    3rd Qu.:2016-05-03 23:47:00.00                     3rd Qu.:1.155e+10  
                    Max.   :2016-05-12 09:56:00.00                     Max.   :1.162e+10  

# Assuming "value" column represents total sleep records
frequency_table <- as.data.frame(table(minute_sleep_clean$value))
frequency_table$Percentage <- frequency_table$Freq / sum(frequency_table$Freq) * 100

ggplot(data = frequency_table, aes(x = Var1, y = Freq)) +
  geom_bar(stat = "identity", fill = "#008080") +
  geom_text(aes(label = paste(Freq, " (", percent(Percentage / 100), ")", sep = "")),
            hjust = 0.5,  vjust = -0.4, color = "black") +
  labs(x = "Total Minutes Records", y = "Frequency",
       title = "User Sleep States: 91% of Minutes Spent Asleep with Minimal Interruptions:",
       subtitle = "Restlessness: 7.4% | Awake: 1.1%") +
  theme_minimal() +
  theme(panel.grid = element_blank(),
        plot.title = element_text(size = 12),
        plot.subtitle = element_text(size = 10, margin = margin(b = 20)))

EDA for hourly_activity_clean

str(hourly_activity_clean)
tibble [22,099 × 6] (S3: tbl_df/tbl/data.frame)
 $ id               : chr [1:22099] "1503960366" "1503960366" "1503960366" "1503960366" ...
 $ activity_hour    : POSIXct[1:22099], format: "2016-04-12 00:00:00" "2016-04-12 01:00:00" "2016-04-12 02:00:00" "2016-04-12 03:00:00" ...
 $ calories         : num [1:22099] 81 61 59 47 48 48 48 47 68 141 ...
 $ total_intensity  : num [1:22099] 20 8 7 0 0 0 0 0 13 30 ...
 $ average_intensity: num [1:22099] 0.333 0.133 0.117 0 0 ...
 $ step_total       : num [1:22099] 373 160 151 0 0 ...
  • Calories integer Total number of estimated calories burned.

  • TotalIntensity: integer Value calculated by adding all the minute-level intensity values that occurred within the hour.

  • AverageIntensity: intensity state exhibited during that hour (TotalIntensity for that ActivityHour divided by 60)

  • StepTotal: Total number of steps taken.

For more detail check : Fitbit data dictionary

hourly_df <-hourly_activity_clean


# Extract "am" or "pm" from the activity_hour column
hourly_df$am_pm <- ifelse(format(hourly_df$activity_hour, "%p") == "AM", "am", "pm")

#Add a column for the hour
hourly_df$hour <- format(hourly_df$activity_hour, "%H")
# Define colors for AM and PM
color_palette <- c("#FFA500", "#ADD8E6")  # Orange for AM, Light Blue for PM

# Custom function to generate the boxplot with dynamically set y-axis limits
generate_boxplot <- function(data, y_var, y_label, limit_factor) {
  y_limit <- quantile(data[[y_var]], 0.95) * limit_factor
  
  ggplot(data, aes(x = hour, y = get(y_var), fill = am_pm)) +
    geom_boxplot(position = position_dodge(0.9), outlier.shape = NA) +
    scale_fill_manual(values = color_palette) +
    labs(title = paste("Median", y_label, "by Hour"),
         x = "Hour",
         y = paste("Median", y_label)) +
    guides(fill = guide_legend(title = NULL)) +  # Remove legend title
    theme_minimal() +
    theme(panel.grid.major.x = element_blank()) +
    coord_cartesian(ylim = c(0, y_limit))
}

# Assuming your dataset is named 'hourly_df1'
data <- hourly_df

# Create the plots with dynamically adjusted y-axis limits
# Adjust the limit_factor as needed (e.g., 1.1, 1.2, etc.)
calories_plot <- generate_boxplot(data, "calories", "Calorie Burn", limit_factor = 1.4)
total_intensity_plot <- generate_boxplot(data, "total_intensity", "Total Intensity", limit_factor = 1.3)
step_total_avg_plot <- generate_boxplot(data, "step_total", "Total Steps", limit_factor = 1.3)

# Print the plots
print(calories_plot)

print(total_intensity_plot)

print(step_total_avg_plot)


# Group by id, hour, and am_pm and summarize the columns
summary_data <- hourly_df %>%
  group_by(id, hour, am_pm) %>%
  summarize(
    calories_avg = mean(calories),
    calories_max = max(calories),
    calories_min = min(calories),
    total_intensity_avg = mean(total_intensity),
    total_intensity_max = max(total_intensity),
    total_intensity_min = min(total_intensity),
    average_intensity_avg = mean(average_intensity),
    step_total_avg = mean(step_total),
    observations_count = n()
  )
`summarise()` has grouped output by 'id', 'hour'. You can override using the `.groups` argument.


# Define colors for AM and PM
color_palette <- c("#FFA500", "#ADD8E6")  # Orange for AM, Light Blue for PM

# Custom function to generate the bar plot with dynamically set y-axis limits
generate_bar_plot <- function(data, y_var, y_label, limit_factor) {
  y_limit <- max(data[[paste0(y_var, "_avg")]]) * limit_factor
  
  ggplot(data, aes(x = hour, y = get(paste0(y_var, "_avg")), fill = am_pm)) +
    geom_bar(stat = "identity", position = "dodge") +
    scale_fill_manual(values = color_palette) +
    labs(title = paste("Average", y_label, "by Hour"),
         x = "Hour",
         y = paste("Average", y_label)) +
    guides(fill = guide_legend(title = NULL)) +  # Remove legend title
    theme_minimal() +
    theme(panel.grid.major.x = element_blank()) +
    coord_cartesian(ylim = c(0, y_limit))
}


# Assuming your dataset is named 'summary_data'
data <- summary_data

# Create the bar plots with dynamically adjusted y-axis limits
calories_plot <- generate_bar_plot(data, "calories", "Calorie Burn", limit_factor = 1.2)
total_intensity_plot <- generate_bar_plot(data, "total_intensity", "Total Intensity", limit_factor = 1.2)
steps_plot <- generate_bar_plot(data, "step_total", "Steps Taken", limit_factor = 1.1)

# Print the plots
print(calories_plot)

print(total_intensity_plot)

print(steps_plot)

# Function to create a box plot with customizable orientation and colors for a given y-axis column
create_custom_box_plot <- function(data, x_axis_col, y_axis_col, orientation = "ver", colors) {
  ggplot(data, aes_string(x = x_axis_col, y = y_axis_col, fill = x_axis_col)) +
    geom_boxplot(width = ifelse(orientation == "ver", 0.2, 0.5),
                 color = "black") +  # Remove fill = "white"
    labs(x = "", y = y_axis_col,
         title = paste("Comparison", x_axis_col, "for", y_axis_col)) +
    scale_fill_manual(values = colors) +
     guides(fill = "none") +  # Remove the legend for fill color
    theme_minimal() +
    if (orientation == "hor") coord_flip()
}

# Call the function with custom colors as a tuple and specify the x-axis label
custom_colors <- c("#FFA500", "#ADD8E6")  # Orange for AM, Light Blue for PM


for (col in c("calories", "total_intensity", "step_total")) {
  plot <- create_custom_box_plot(hourly_df, x_axis_col = "am_pm", y_axis_col = col, orientation = "hor", colors = custom_colors)
  print(plot)
}

NA
NA

With the code provided, you can gain several insights into user behavior and activity patterns based on the “hourly_df” dataset. Some of the insights you can obtain are:

Hourly Activity Patterns: You can observe how user activity varies throughout the day. The calories_avg, total_intensity_avg, and average_intensity_avg columns will give you the average calorie burn, total intensity, and average intensity for each hour, respectively. This can help identify peak activity hours and periods of lower activity.

Variability in Activity: The calories_max, calories_min, total_intensity_max, and total_intensity_min columns will provide information about the maximum and minimum values of calorie burn and total intensity recorded during each hour. This can help you understand the range of activity levels and how much the activity varies from hour to hour.

Average Steps per Hour: The step_total_avg column will give you the average number of steps taken during each hour. This can help you identify the typical step count during different times of the day.

Observations Count: The observations_count column will show the number of data points (observations) available for each “id,” “hour,” and “am_pm” group. This can help you assess the data density and identify hours with more or fewer data points, which may influence the reliability of the insights.

AM vs. PM Activity: The “am_pm” column indicates whether the activity occurred during the morning (AM) or afternoon/evening (PM). You can compare the activity patterns between these two periods and explore any differences in user behavior during these times.

Individual User Insights: By grouping the data by “id,” you can also gain insights into each individual user’s activity patterns. You can assess their average calorie burn, intensity, and steps during various hours.

Overall, these insights can help you understand how users engage in physical activity throughout the day, identify peak activity hours, and detect any patterns or trends in their behavior. This information can be valuable for designing personalized fitness plans, optimizing activity programs, and making data-driven decisions to improve health and well-being.

EDA for seconds_heartrate_clean

str(seconds_heartrate_clean)
tibble [2,483,658 × 3] (S3: tbl_df/tbl/data.frame)
 $ id        : chr [1:2483658] "2022484408" "2022484408" "2022484408" "2022484408" ...
 $ date_time : POSIXct[1:2483658], format: "2016-04-12 07:21:00" "2016-04-12 07:21:05" "2016-04-12 07:21:10" "2016-04-12 07:21:20" ...
 $ heart_rate: num [1:2483658] 97 102 105 103 101 95 91 93 94 93 ...
seconds_heartrate_clean %>% summary()
      id              date_time                        heart_rate    
 Length:2483658     Min.   :2016-04-12 00:00:00.00   Min.   : 36.00  
 Class :character   1st Qu.:2016-04-19 06:18:10.00   1st Qu.: 63.00  
 Mode  :character   Median :2016-04-26 20:28:50.00   Median : 73.00  
                    Mean   :2016-04-26 19:43:52.24   Mean   : 77.33  
                    3rd Qu.:2016-05-04 08:00:20.00   3rd Qu.: 88.00  
                    Max.   :2016-05-12 16:20:00.00   Max.   :203.00  
  n_distinct(seconds_heartrate_clean$id)
[1] 14


# Group by 'id' and calculate the average heart rate for each user
average_heart_rate <- seconds_heartrate_clean %>%
  group_by(id) %>%
  summarise(average_heart_rate = mean(heart_rate, na.rm = TRUE))

# Bar Plot with smaller bars and custom breaks on the heart rate axis
bar_plot <- ggplot(average_heart_rate, aes(x = id, y = average_heart_rate)) +
  geom_bar(stat = "identity", fill = "#ADD8E6", width = 0.8) +  # Adjust the width here (e.g., 0.5 for smaller bars)
  labs(x = "User ID", y = "Average Heart Rate", title = "Average Heart Rate for Each User") +
  theme_minimal() + coord_flip() +
  scale_y_continuous(breaks = seq(0, 100, 10), limits = c(0, 100))+ # Set custom breaks and limits for the y-axis 
  geom_hline(yintercept = 60, linetype = "dashed", color = "green") +
  geom_hline(yintercept = 100, linetype = "dashed", color = "green") 

# Display the bar plot
print(bar_plot)

NA
NA

EDA for weight_logs_clean

str(weight_logs_clean)
tibble [67 × 8] (S3: tbl_df/tbl/data.frame)
 $ id              : chr [1:67] "1503960366" "1503960366" "1927972279" "2873212765" ...
 $ date_time       : POSIXct[1:67], format: "2016-05-02 23:59:59" "2016-05-03 23:59:59" "2016-04-13 01:08:52" "2016-04-21 23:59:59" ...
 $ weight_kg       : num [1:67] 52.6 52.6 133.5 56.7 57.3 ...
 $ weight_pounds   : num [1:67] 116 116 294 125 126 ...
 $ fat             : num [1:67] 22 0 0 0 0 25 0 0 0 0 ...
 $ bmi             : num [1:67] 22.6 22.6 47.5 21.5 21.7 ...
 $ is_manual_report: logi [1:67] TRUE TRUE FALSE TRUE TRUE TRUE ...
 $ log_id          : num [1:67] 1.46e+12 1.46e+12 1.46e+12 1.46e+12 1.46e+12 ...
  • Fat:Body fat percentage recorded.

  • BMI: Measure of body mass index based on the height and weight in the participant’s Fitbit.com profile.

  • isManualReport: If the data for this weigh-in was done manually (TRUE), or if data was measured and synced directly to Fitbit.com from a connected scale (FALSE).

For more detail check : Fitbit data dictionary

weight_logs_clean %>% summary()
      id              date_time                        weight_kg      weight_pounds        fat               bmi        is_manual_report
 Length:67          Min.   :2016-04-12 06:47:11.00   Min.   : 52.60   Min.   :116.0   Min.   : 0.0000   Min.   :21.45   Mode :logical   
 Class :character   1st Qu.:2016-04-19 15:19:45.00   1st Qu.: 61.40   1st Qu.:135.4   1st Qu.: 0.0000   1st Qu.:23.96   FALSE:26        
 Mode  :character   Median :2016-04-27 23:59:59.00   Median : 62.50   Median :137.8   Median : 0.0000   Median :24.39   TRUE :41        
                    Mean   :2016-04-27 15:39:54.27   Mean   : 72.04   Mean   :158.8   Mean   : 0.7015   Mean   :25.19                   
                    3rd Qu.:2016-05-04 15:24:10.50   3rd Qu.: 85.05   3rd Qu.:187.5   3rd Qu.: 0.0000   3rd Qu.:25.56                   
                    Max.   :2016-05-12 23:59:59.00   Max.   :133.50   Max.   :294.3   Max.   :25.0000   Max.   :47.54                   
     log_id         
 Min.   :1.460e+12  
 1st Qu.:1.461e+12  
 Median :1.462e+12  
 Mean   :1.462e+12  
 3rd Qu.:1.462e+12  
 Max.   :1.463e+12  
# Create boxplots for "bmi" and "weight_pounds"

columns_to_analyze <- c("bmi", "weight_pounds")

# Call the function to create boxplots 
create_boxplots_in_one_output(weight_logs_clean, columns_to_analyze, decimal_places = 2)


entry_count <- weight_logs_clean %>%
  group_by(id, is_manual_report) %>%
  summarize(entry_count = n(), .groups = "keep") %>%
  arrange (- entry_count)

print(entry_count)
NA
NA
weight_logs_clean %>% filter(fat != 0)
average_bmi_weight <- weight_logs_clean %>%
  group_by(is_manual_report) %>%
  summarize(mean_bmi = mean(bmi, na.rm = TRUE),
            mean_weight_pounds = mean(weight_pounds, na.rm = TRUE),
            entry_count = n(), 
            .groups = "keep") 

print(average_bmi_weight)
NA


create_custom_box_plot(weight_logs_clean , x_axis_col = "is_manual_report", y_axis_col = "weight_pounds", orientation = "hor", colors = custom_colors)

# Use the remove_outliers function we created previously to remove the outliers

columns_with_outliers <- c("weight_pounds", "bmi")


# Loop through each column and remove outliers
for (col in columns_with_outliers) {
  weight_logs_clean <- remove_outliers(weight_logs_clean, col)
}
dim(weight_logs_clean)
[1] 64  8
create_custom_box_plot(weight_logs_clean , x_axis_col = "is_manual_report", y_axis_col = "weight_pounds", orientation = "hor", colors = custom_colors)

Observations:

  • Only two users reported fat percentage
  • It appears that users that log their weight data manually have a lower median weight than users that synced their weight from other devices.
  • Users that that log their weight data manuall had more entries

Calculate the total average

total_average <- sum(averages)

Calculate the proportions

proportions <- averages / total_average

Create the new dataframe with modified row names

overall_average_df<- data.frame(Average = averages, Percentage = proportions * 100)

User Engagement: The difference in distinct IDs across datasets could reflect different levels of user engagement with different types of data. For example, the “weight_logs_clean” dataset having fewer distinct IDs might indicate that fewer users are actively logging their weight data compared to other types of data.

Insights and recommendations

https://www.cdc.gov/mmwr/volumes/68/wr/mm6823a1.htm

file:///Users/vivianbarros/Desktop/Physical_Activity_Guidelines_2nd_edition.pdf

Appendix

https://stackoverflow.com/questions/13035834/plot-every-column-in-a-data-frame-as-a-histogram-on-one-page-using-ggplot

https://stackoverflow.com/questions/13035834/plot-every-column-in-a-data-frame-as-a-histogram-on-one-page-using-ggplot

Another source:

https://www.kaggle.com/datasets/arashnic/fitbit/discussion/313589?search=data

#paper https://dl.acm.org/doi/pdf/10.1145/3339825.3394926

this is it: physical innactivity. Plot a barplot with percentages. https://www.cdc.gov/physicalactivity/data/inactivity-prevalence-maps/index.html#Race-Ethnicity

information about physical activity guidlines (sex and age):

https://www.cdc.gov/nchs/products/databriefs/db443.htm Elgaddal N, Kramarow EA, Reuben C. Physical activity among adults aged 18 and over: United States, 2020. NCHS Data Brief, no 443. Hyattsville, MD: National Center for Health Statistics. 2022. DOI: https://dx.doi.org/10.15620/cdc:120213

Reference:

Categorical, ordinal, interval, and ratio variables : https://www.graphpad.com/guides/prism/latest/statistics/the_different_kinds_of_variabl.htm

Add density line to histogram: https://r-coder.com/density-plot-r

——————— showing notebook in github

convert to jupyter notbook option: https://medium.datadriveninvestor.com/transforming-your-rmd-to-ipynb-file-r-markdown-to-python-jupyter-b1306646f50b

Hey all,

Sorry if I’m misunderstanding here, but I have been knitting the .Rmd notebook to a .md file within RStudio, and it seems to display very well in GitHub. You can see an example in my repo to see if I’m on track with this thread.

The links below give the explanation. Short Version:

change “output=html_document” to “output=github_document”

knit the document push the .md file to GitHub instead of the .Rmd be sure to push the ’_files’ folder to include any images https://rmarkdown.rstudio.com/github_document_format.html https://gist.github.com/JoshuaTPierce/b919168421b40e06481080eb53c3fb2f

LS0tCnRpdGxlOiAiQmVsbGFiZWF0IENhc2Ugc3R1ZHkgZHJhZnQxIgpvdXRwdXQ6CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdApkZl9wcmludDogcGFnZWQKLS0tCiMgUHJlcGFyZSBhbmQgUHJlcHJvY2VzcyBQaGFzZQoKCiMgTWV0YSBkYXRhIAoKIFtGaXRiaXQgZGF0YSBkaWN0aW9uYXJ5IF0oaHR0cHM6Ly93d3cuZml0YWJhc2UuY29tL3Jlc291cmNlcy9rbm93bGVkZ2UtYmFzZS9leHBvcnRpbmctZGF0YS9kYXRhLWRpY3Rpb25hcmllcy8pCgoKIyBJbXBvcnQgbGlicmFyaWVzIApgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBpbmNsdWRlcyBnZ3Bsb3QyCmxpYnJhcnkoc2tpbXIpICMgIHByb3ZpZGVzIGEgY29tcGFjdCBhbmQgaW5mb3JtYXRpdmUgc3VtbWFyeSBvZiB5b3VyIGRhdGEgZnJhbWUgb3IgZGF0YXNldApsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShqYW5pdG9yKSAjIHNldCBvZiB1dGlsaXR5IGZ1bmN0aW9ucyBmb3IgZGF0YSBjbGVhbmluZyBhbmQgZGF0YSBmcmFtZSB0aWR5aW5nIHRhc2tzCmxpYnJhcnkoUkNvbG9yQnJld2VyKSAjIENvbG9yIHBhbGV0dGVzIGZvciBkYXRhIHZpc3VhbGl6YXRpb24KbGlicmFyeShnZ2NvcnJwbG90KSAjIFZpc3VhbGl6ZSBjb3JyZWxhdGlvbiBtYXRyaWNlcyB1c2luZyBnZ3Bsb3QyCmxpYnJhcnkoc2NhbGVzKSAjIGZvcm1hdHRpbmcgYW5kIHRyYW5zZm9ybWluZyBkYXRhIGZvciB2aXN1YWxpemF0aW9ucwoKCiMgZGlzcGxheS5icmV3ZXIuYWxsKGNvbG9yYmxpbmRGcmllbmRseSA9IFRSVUUpCmBgYAoKCiMgTG9hZCBkYXRhc2V0cyAKCmBgYHtyfQojIENsZWFuIGVudmlyb25tZW50CnJtKGxpc3QgPSBscygpKQoKZGFpbHlfYWN0aXZpdHkgPC0KICByZWFkX2Nzdigib3JpZ2luYWxfZGF0YS9kYWlseUFjdGl2aXR5X21lcmdlZC5jc3YiLAogICAgdHJpbV93cyA9IFRSVUUsCiAgICBzaG93X2NvbF90eXBlcyA9IEZBTFNFCiAgKQoKZGFpbHlfc2xlZXAgPC0gcmVhZF9jc3YoIm9yaWdpbmFsX2RhdGEvc2xlZXBEYXlfbWVyZ2VkLmNzdiIsCiAgdHJpbV93cyA9IFRSVUUsCiAgc2hvd19jb2xfdHlwZXMgPSBGQUxTRQopCgpob3VybHlfY2Fsb3JpZXMgPC0KICByZWFkX2Nzdigib3JpZ2luYWxfZGF0YS9ob3VybHlDYWxvcmllc19tZXJnZWQuY3N2IiwKICAgIHRyaW1fd3MgPSBUUlVFLAogICAgc2hvd19jb2xfdHlwZXMgPSBGQUxTRQogICkKCmhvdXJseV9pbnRlbnNpdGllcyA8LQogIHJlYWRfY3N2KCJvcmlnaW5hbF9kYXRhL2hvdXJseUludGVuc2l0aWVzX21lcmdlZC5jc3YiLAogICAgdHJpbV93cyA9IFRSVUUsCiAgICBzaG93X2NvbF90eXBlcyA9IEZBTFNFCiAgKQpob3VybHlfc3RlcHMgPC0KICByZWFkX2Nzdigib3JpZ2luYWxfZGF0YS9ob3VybHlTdGVwc19tZXJnZWQuY3N2IiwKICAgIHRyaW1fd3MgPSBUUlVFLAogICAgc2hvd19jb2xfdHlwZXMgPSBGQUxTRQogICkKCm1pbnV0ZV9zbGVlcCA8LQogIHJlYWRfY3N2KCJvcmlnaW5hbF9kYXRhL21pbnV0ZVNsZWVwX21lcmdlZC5jc3YiLAogICAgdHJpbV93cyA9IFRSVUUsCiAgICBzaG93X2NvbF90eXBlcyA9IEZBTFNFCiAgKQoKd2VpZ2h0X2xvZ3MgPC0KICByZWFkX2Nzdigib3JpZ2luYWxfZGF0YS93ZWlnaHRMb2dJbmZvX21lcmdlZC5jc3YiLAogICAgdHJpbV93cyA9IFRSVUUsCiAgICBzaG93X2NvbF90eXBlcyA9IEZBTFNFCiAgKQoKc2Vjb25kc19oZWFydHJhdGUgPC0KICByZWFkX2Nzdigib3JpZ2luYWxfZGF0YS9oZWFydHJhdGVfc2Vjb25kc19tZXJnZWQuY3N2IiwKICAgIHRyaW1fd3MgPSBUUlVFLAogICAgc2hvd19jb2xfdHlwZXMgPSBGQUxTRQogICkKCiMgUmVtb3ZlIHRyYWlsaW5nIHNwYWNlcyAodHJpbV93cyA9IFRSVUUpCmBgYAoKCgojIENsZWFuIGRhdGEgc2V0cwoKIyMgQ2xlYW4gdGhlIGRhaWx5X2FjdGl2aXR5IGRhdGEgc2V0CmBgYHtyfQojIENoZWNrIGRhaWx5X2FjdGl2aXR5IGRhdGEgc2V0IGJlZm9yZSBjbGVhbmluZwpnbGltcHNlKGRhaWx5X2FjdGl2aXR5KQoKIyBDaGVjayBtaXNzaW5nIHZhbHVlcyBhbmQgZHVwbGljYXRlcwpjYXQoCiAgIlxuIiwKICAiTWlzc2luZyB2YWx1ZXM6IiwKICBzdW0oaXMubmEoZGFpbHlfYWN0aXZpdHkpKSwKICAiXG4iLAogICJEdXBsaWNhdGUgdmFsdWVzOiIsCiAgc3VtKGR1cGxpY2F0ZWQoZGFpbHlfYWN0aXZpdHkpKSwKICAiXG4iLAogICJVbmlxdWUgSWRzOiIsCiAgbl9kaXN0aW5jdChkYWlseV9hY3Rpdml0eSRJZCkKKQpgYGAKCkxldCB1cyBjbGVhbjoKLSBDaGFuZ2UgY29sdW1uIG5hbWVzIHRvIGxvd2VyIGNhc2UgYmVjYXVzZSBSIGlzIGNhc2Ugc2Vuc2l0aXZlCi0gQ2hhbmdlICJJZCIgZnJvbSBkb3VibGUgdG8gYSBjaGFyYWN0ZXIgYmVjYXVzZSB0aGUgbnVtYmVyIHJlcHJlc2VudHMgYSBjYXRlZ29yeQotIENoYW5nZSAiQWN0aXZpdHlEYXRlIiBmcm9tIGNoYXIgdG8gZGF0ZSAKCmBgYHtyfQojIENsZWFuIGRhaWx5X2FjdGl2aXR5IGRhdGEgc2V0CgpkYWlseV9hY3Rpdml0eSA8LQogICMgQ2xlYW4gY29sdW1uIG5hbWVzCiAgY2xlYW5fbmFtZXMoZGFpbHlfYWN0aXZpdHkpICU+JQogICMgQ29ycmVjdCBjb2x1bW4gdHlwZXMKICBtdXRhdGUoaWQgPSBhcy5jaGFyYWN0ZXIoaWQpKSAlPiUgIyBmcm9tIGRvdWJsZSB0byBjaHIKICBtdXRhdGUoYWN0aXZpdHlfZGF0ZSA9IGFzLkRhdGUoYWN0aXZpdHlfZGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gIiVtLyVkLyVZIikpICU+JSAjIGZyb20gY2hyIHRvIGRhdGUKICAjIFJlbW92ZSBkdXBsaWNhdGUgcm93cwogIGRpc3RpbmN0KCkKCiMgQ2hlY2sgZGFpbHlfYWN0aXZpdHkgZGF0YSBzZXQgYWZ0ZXIgY2xlYW5pbmcKZ2xpbXBzZShkYWlseV9hY3Rpdml0eSkKCiMgQ2hlY2sgbWlzc2luZyB2YWx1ZXMgYW5kIGR1cGxpY2F0ZXMgYWZ0ZXIgY2xlYW5pbmcKY2F0KCJcbiIsCiAgICAiTWlzc2luZyB2YWx1ZXM6IiwKICAgIHN1bShpcy5uYShkYWlseV9hY3Rpdml0eSkpLAogICAgIlxuIiwKICAgICJEdXBsaWNhdGUgdmFsdWVzOiIsCiAgICBzdW0oZHVwbGljYXRlZChkYWlseV9hY3Rpdml0eSkpKQpgYGAKCgoKYGBge3J9CiMgTGV0IHVzIHByaW50IHN1bW1hcnkgc3RhdGlzdGljIHRvIGhhdmUgYSBiZXR0ZXIgaWRlYSBvZiB0aGUgZGF0YSBzZXQKZGFpbHlfYWN0aXZpdHkgJT4lCiAgc3VtbWFyeSgpCmBgYAoKVGhpcyBzdW1tYXJ5IGhlbHBzIHVzIGV4cGxvcmUgcXVpY2tseSBlYWNoIGF0dHJpYnV0ZS4gV2Ugbm90aWNlIHRoYXQgc29tZSBhdHRyaWJ1dGVzIGhhdmUgbWluaW11bSB2YWx1ZSBvZiB6ZXJvICh0b3RhbF9zdGVwLCB0b3RhbF9kaXN0YW5jZSwgY2Fsb3JpZXMpLiAKTGV0IHVzIGV4cGxvcmUgdGhpcyBvYnNlcnZhdGlvbi4KCgpgYGB7cn0KIyBDaGVjayB3aGVyZSB0b3RhbF9zdGVwcyBpcyB6ZXJvCmZpbHRlcihkYWlseV9hY3Rpdml0eSwgdG90YWxfc3RlcHMgPT0gMCkKYGBgCldlIGZvdW5kIDc3IG9ic2VydmF0aW9ucyB3aGVyZSB0b3RhbF9zdGVwcyBpcyB6ZXJvLiBXZSBzaG91bGQgZGVsZXRlIHRoZXNlIG9ic2VydmF0aW9ucyBzbyB0aGF0IHRoZXkgZG8gbm90IGFmZmVjdCBvdXIgdGhlIG1lYW4gYW5kIG1lZGlhbi4KSWYgdG90YWxfc3RlcCBpcyB6ZXJvIHRoYXQgbWVhbnMgdGhhdCB0aGUgcGVyc29uIGRpZCBub3Qgd2VhciB0aGUgRml0Yml0LgoKYGBge3J9CiMgQ2hlY2sgd2hlcmUgY2Fsb3JpZXMgaXMgemVybwpmaWx0ZXIoZGFpbHlfYWN0aXZpdHksIGNhbG9yaWVzID09IDApCmBgYAoKYGBge3J9CiMgQ2hlY2sgd2hlcmUgdG90YWxfZGlzdGFuY2UgaXMgemVybwpmaWx0ZXIoZGFpbHlfYWN0aXZpdHksIHRvdGFsX2Rpc3RhbmNlID09IDApCmBgYAogRnJvbSBvdXIgaW5zcGVjdGlvbiBhYm92ZSwgd2UgY2FuIHNlZSB0aGF0IHdlIGp1c3QgbmVlZCB0byBkZWxldGUgdGhlIGVudHJpZXMgd2hlcmUgdG90YWxfc3RlcHMgaXMgemVybyBhbmQgd2lsbCB0YWtlIHRha2UgY2FyZSBvZiB0aGUgcmVzdC4KCgpgYGB7cn0KZGFpbHlfYWN0aXZpdHlfY2xlYW4gPC0KICBmaWx0ZXIoZGFpbHlfYWN0aXZpdHksCiAgICAgICAgIHRvdGFsX3N0ZXBzICE9IDAsCiAgICAgICAgIHRvdGFsX2Rpc3RhbmNlICE9IDAsCiAgICAgICAgIGNhbG9yaWVzICE9IDApCmRhaWx5X2FjdGl2aXR5X2NsZWFuCgpgYGAKCgoKCmBgYHtyfQpuYW1lcyhkYWlseV9hY3Rpdml0eSkKYGBgCgoKYGBge3J9CiMgQ2hlY2sgdGhlIGF0dHJpYnV0ZXMgYWdhaW4KCmNhdCgiQmVmb3JlIGRlbGV0aW5nIHRoZSBlbnRyaWVzXG5cbiIpCnNlbGVjdChkYWlseV9hY3Rpdml0eSx0b3RhbF9zdGVwcyx0b3RhbF9kaXN0YW5jZSxjYWxvcmllcykgJT4lCiAgc3VtbWFyeSgpCgpjYXQoIlxuXG5cbiIsCiAgICAiXHRcdCB2cyIsCiAgICAiXG5cblxuIikKCgpjYXQoIkFmdGVyIGRlbGV0aW5nIHRoZSBlbnRyaWVzXG5cbiIpCnNlbGVjdChkYWlseV9hY3Rpdml0eV9jbGVhbiwgdG90YWxfc3RlcHMsIHRvdGFsX2Rpc3RhbmNlLCBjYWxvcmllcykgJT4lCiAgc3VtbWFyeSgpCmBgYApXZSBjYW4gc2VlIHRoYXQgdGhlIG9ic2VydmF0aW9uIHdlIHJlbW92ZWQgYWZmZWN0ZWQgb3VyIG1lYW4gYW5kIG1lZGlhbi4KCgoKIyMgQ2xlYW4gdGhlIGRhaWx5X3NsZWVwIGRhdGEgc2V0CmBgYHtyfQojIENoZWNrIGRhaWx5X3NsZWVwIGRhdGEgc2V0IGJlZm9yZSBjbGVhbmluZwpnbGltcHNlKGRhaWx5X3NsZWVwKQoKIyBDaGVjayBtaXNzaW5nIHZhbHVlcyBhbmQgZHVwbGljYXRlcwpjYXQoIlxuIiwKICAgICJNaXNzaW5nIHZhbHVlczoiLAogICAgc3VtKGlzLm5hKGRhaWx5X3NsZWVwKSksCiAgICAiXG4iLAogICAgIkR1cGxpY2F0ZSB2YWx1ZXM6IiwKICAgIHN1bShkdXBsaWNhdGVkKGRhaWx5X3NsZWVwKSksCiAgIlxuIiwKICAiVW5pcXVlIElkczoiLAogIG5fZGlzdGluY3QoZGFpbHlfc2xlZXAkSWQpCiAgICApCmBgYAoKTGV0IHVzIGNsZWFuOgoKLSBDaGFuZ2UgY29sdW1uIG5hbWVzIHRvIGxvd2VyIGNhc2UgYmVjYXVzZSBSIGlzIGNhc2Ugc2Vuc2l0aXZlCi0gQ2hhbmdlICJJZCIgZnJvbSBkb3VibGUgdG8gYSBjaGFyYWN0ZXIgYmVjYXVzZSB0aGUgbnVtYmVyIHJlcHJlc2VudHMgYSBjYXRlZ29yeQotIENoYW5nZSAiU2xlZXBEYXkiIGZyb20gY2hhciB0byBkYXRlLiBTaW5jZSB0aGUgdGltZSBjb21wb25lbnQgb2YgdGhpcyBjb2x1bW4gaXMgdGhlCiAgc2FtZSBmb3IgZWFjaCBvYnNlcnZhdGlvbiIxMjowMDowMCBBTSIsIHdlIGNhbiByZW1vdmUgaXQuIFRoaXMgd2lsbCBoZWxwcyB1cyBtZXJnZWQgdGhpcyAKICBkYXRhIHNldCB3aXRoIGRhaWx5X2FjdGl2aXR5IGxhdGVyCi0gRGVsZXRlIGR1cGxpY2F0ZXMgKDMgb2JzZXJ2YXRpb25zIGFyZSBkdXBsaWNhdGVzKQoKCgpgYGB7cn0KIyBDbGVhbiBkYWlseV9zbGVlcCBkYXRhIHNldAoKZGFpbHlfc2xlZXBfY2xlYW4gPC0KICAjIENsZWFuIGNvbHVtbiBuYW1lcwogIGNsZWFuX25hbWVzKGRhaWx5X3NsZWVwKSAlPiUKICAjIENvcnJlY3QgY29sdW1uIHR5cGVzCiAgbXV0YXRlKGlkID0gYXMuY2hhcmFjdGVyKGlkKSkgJT4lICMgZnJvbSBkb3VibGUgdG8gY2hyCiAgbXV0YXRlKHNsZWVwX2RheSA9IGFzLkRhdGUoc2xlZXBfZGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCA9ICIlbS8lZC8lWSIpKSAlPiUgIyBmcm9tIGNociB0byBkYXRlCiAgIyBSZW1vdmUgZHVwbGljYXRlIHJvd3MKICBkaXN0aW5jdCgpCgojIENoZWNrIGNsZWFuIGRhaWx5X3NsZWVwIGRhdGEgc2V0CmdsaW1wc2UoZGFpbHlfc2xlZXBfY2xlYW4pCgojIENoZWNrIG1pc3NpbmcgdmFsdWVzIGFuZCBkdXBsaWNhdGVzIGFmdGVyIGNsZWFuaW5nCmNhdCgiXG4iLAogICAgIk1pc3NpbmcgdmFsdWVzOiIsCiAgICBzdW0oaXMubmEoZGFpbHlfc2xlZXBfY2xlYW4pKSwKICAgICJcbiIsCiAgICAiRHVwbGljYXRlIHZhbHVlczoiLAogICAgc3VtKGR1cGxpY2F0ZWQoZGFpbHlfc2xlZXBfY2xlYW4pKSkKYGBgCgoKCiMjIENsZWFuIHRoZSBob3VybHkgZGF0YSBzZXRzIChob3VybHlfY2Fsb3JpZXMsIGhvdXJseV9pbnRlbnNpdGllcywgYW5kIGhvdXJseV9zdGVwcykKCgpgYGB7cn0KIyBDaGVjayBob3VybHlfY2Fsb3JpZXMgZGF0YSBzZXQgYmVmb3JlIGNsZWFuaW5nCmdsaW1wc2UoaG91cmx5X2NhbG9yaWVzKQoKIyBDaGVjayBtaXNzaW5nIHZhbHVlcyBhbmQgZHVwbGljYXRlcwpjYXQoIlxuIiwKICAgICJNaXNzaW5nIHZhbHVlczoiLAogICAgc3VtKGlzLm5hKGhvdXJseV9jYWxvcmllcykpLAogICAgIlxuIiwKICAgICJEdXBsaWNhdGUgdmFsdWVzOiIsCiAgICBzdW0oZHVwbGljYXRlZChob3VybHlfY2Fsb3JpZXMpKSkKYGBgCgpgYGB7cn0KIyBDaGVjayBob3VybHlfaW50ZW5zaXRpZXMgZGF0YSBzZXQgYmVmb3JlIGNsZWFuaW5nCmdsaW1wc2UoaG91cmx5X2ludGVuc2l0aWVzKQoKIyBDaGVjayBtaXNzaW5nIHZhbHVlcyBhbmQgZHVwbGljYXRlcwpjYXQoIlxuIiwKICAgICJNaXNzaW5nIHZhbHVlczoiLAogICAgc3VtKGlzLm5hKGhvdXJseV9pbnRlbnNpdGllcykpLAogICAgIlxuIiwKICAgICJEdXBsaWNhdGUgdmFsdWVzOiIsCiAgICBzdW0oZHVwbGljYXRlZChob3VybHlfaW50ZW5zaXRpZXMpKSkKYGBgCgoKCmBgYHtyfQojIENoZWNrIGhvdXJseV9zdGVwcyBkYXRhIHNldCBiZWZvcmUgY2xlYW5pbmcKZ2xpbXBzZShob3VybHlfc3RlcHMpCgojIENoZWNrIG1pc3NpbmcgdmFsdWVzIGFuZCBkdXBsaWNhdGVzCmNhdCgiXG4iLAogICAgIk1pc3NpbmcgdmFsdWVzOiIsCiAgICBzdW0oaXMubmEoaG91cmx5X3N0ZXBzKSksCiAgICAiXG4iLAogICAgIkR1cGxpY2F0ZSB2YWx1ZXM6IiwKICAgIHN1bShkdXBsaWNhdGVkKGhvdXJseV9zdGVwcykpKQpgYGAKCiMjIyBKb2luIGhvdXJseSBkYXRhIHNldHMgdG8gY3JlYXRlIGEgaG91cmx5X2FjdGl0dml0eSBkYXRhIHNldApUaGVzZSBkYXRhIHNldHMgc2hhcmVkIHRoZSBzYW1lIElkIGFuZCBBY3Rpdml0eV9ob3VyLCBsZXQgdXMgam9pbiB0aGVtIGludG8gYSBuZXcgZGF0YSBzZXQgKGhvdXJseV9hY3Rpdml0eSkgYmVmb3JlIHdlIGNsZWFuIHRoZW0uCgoKCmBgYHtyfQojIEpvaW4gdGhlIGhvdXJseSBkYXRhIHNldHMgKGhvdXJseV9jYWxvcmllcywgaG91cmx5X2ludGVuc2l0aWVzLCBhbmQgaG91cmx5X3N0ZXBzKQoKaG91cmx5X2FjdGl2aXR5IDwtCiAgaW5uZXJfam9pbihob3VybHlfY2Fsb3JpZXMsCiAgICAgICAgICAgICBob3VybHlfaW50ZW5zaXRpZXMsCiAgICAgICAgICAgICBieSA9IGMoIklkIiwgIkFjdGl2aXR5SG91ciIpKQoKaG91cmx5X2FjdGl2aXR5IDwtCiAgaW5uZXJfam9pbihob3VybHlfYWN0aXZpdHksIGhvdXJseV9zdGVwcywgYnkgPSBjKCJJZCIsICJBY3Rpdml0eUhvdXIiKSkKYGBgCgoKCmBgYHtyfQojIENoZWNrIGhvdXJseV9hY3Rpdml0eSBkYXRhIHNldCBiZWZvcmUgY2xlYW5pbmcKZ2xpbXBzZShob3VybHlfYWN0aXZpdHkpCgojIENoZWNrIG1pc3NpbmcgdmFsdWVzIGFuZCBkdXBsaWNhdGVzCmNhdCgiXG4iLAogICAgIk1pc3NpbmcgdmFsdWVzOiIsCiAgICBzdW0oaXMubmEoaG91cmx5X2FjdGl2aXR5KSksCiAgICAiXG4iLAogICAgIkR1cGxpY2F0ZSB2YWx1ZXM6IiwKICAgIHN1bShkdXBsaWNhdGVkKGhvdXJseV9hY3Rpdml0eSkpKQpgYGAKCkxldCB1cyBjbGVhbjoKCi0gQ2hhbmdlIGNvbHVtbiBuYW1lcyB0byBsb3dlciBjYXNlIGJlY2F1c2UgUiBpcyBjYXNlIHNlbnNpdGl2ZQotIENoYW5nZSAiSWQiIGZyb20gZG91YmxlIHRvIGEgY2hhcmFjdGVyIGJlY2F1c2UgdGhlIG51bWJlciByZXByZXNlbnRzIGEgY2F0ZWdvcnkKLSBDaGFuZ2UgIkFjdGl2aXR5SG91ciIgZnJvbSBjaGFyIHRvIGRhdGV0aW1lCgpOb3RlOlRoZSBkZWZhdWx0IHRpbWV6b25lIGlzIFVUQy4KCgoKYGBge3J9CiMgQ2xlYW4gaG91cmx5X2FjdGl2aXR5IGRhdGEgc2V0Cgpob3VybHlfYWN0aXZpdHlfY2xlYW4gPC0KICAjIENsZWFuIGNvbHVtbiBuYW1lcwogIGNsZWFuX25hbWVzKGhvdXJseV9hY3Rpdml0eSkgJT4lCiAgIyBDb3JyZWN0IGNvbHVtbiB0eXBlcwogIG11dGF0ZShpZCA9IGFzLmNoYXJhY3RlcihpZCkpICU+JSAjIGZyb20gZG91YmxlIHRvIGNocgogIG11dGF0ZShhY3Rpdml0eV9ob3VyID0gYXNfZGF0ZXRpbWUoYWN0aXZpdHlfaG91ciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCA9ICIlbS8lZC8lWSAlSTolTTolUyAlcCIpKSAlPiUgIyBmcm9tIGNociB0byBkYXRldGltZQogICMgUmVtb3ZlIGR1cGxpY2F0ZSByb3dzCiAgZGlzdGluY3QoKQoKIyBDaGVjayBjbGVhbiBkYWlseV9hY3Rpdml0eSBkYXRhIHNldApnbGltcHNlKGhvdXJseV9hY3Rpdml0eV9jbGVhbikKCgojIENoZWNrIG1pc3NpbmcgdmFsdWVzIGFuZCBkdXBsaWNhdGVzIGFmdGVyIGNsZWFuaW5nCmNhdCgiXG4iLAogICAgIk1pc3NpbmcgdmFsdWVzOiIsCiAgICBzdW0oaXMubmEoaG91cmx5X2FjdGl2aXR5X2NsZWFuKSksCiAgICAiXG4iLAogICAgIkR1cGxpY2F0ZSB2YWx1ZXM6IiwKICAgIHN1bShkdXBsaWNhdGVkKGhvdXJseV9hY3Rpdml0eV9jbGVhbikpKQoKIyBhc19kYXRldGltZSgpIGNvbnZlcnRzIHdpdGggZGVmYXVsdCB0aW1lem9uZSA9ICJVVEMiCmBgYAoKCgoKCiMjIENsZWFuIHRoZSBtaW51dGVfc2xlZXAgZGF0YSBzZXQKCmBgYHtyfQojIENoZWNrIG1pbnV0ZV9zbGVlcCBkYXRhIHNldCBiZWZvcmUgY2xlYW5pbmcKZ2xpbXBzZShtaW51dGVfc2xlZXApCgojIENoZWNrIG1pc3NpbmcgdmFsdWVzIGFuZCBkdXBsaWNhdGVzCmNhdCgiXG4iLAogICAgIk1pc3NpbmcgdmFsdWVzOiIsCiAgICBzdW0oaXMubmEobWludXRlX3NsZWVwKSksCiAgICAiXG4iLAogICAgIkR1cGxpY2F0ZSB2YWx1ZXM6IiwKICAgIHN1bShkdXBsaWNhdGVkKG1pbnV0ZV9zbGVlcCkpLAogICAgIlxuIiwKICAiVW5pcXVlIElkczoiLAogIG5fZGlzdGluY3QobWludXRlX3NsZWVwJElkKSkKCmBgYAoKTGV0IHVzIGNsZWFuOgoKLSBDaGFuZ2UgY29sdW1uIG5hbWVzIHRvIGxvd2VyIGNhc2UgYmVjYXVzZSBSIGlzIGNhc2Ugc2Vuc2l0aXZlCi0gQ2hhbmdlICJJZCIgZnJvbSBkb3VibGUgdG8gYSBjaGFyYWN0ZXIgYmVjYXVzZSB0aGUgbnVtYmVyIHJlcHJlc2VudHMgYSBjYXRlZ29yeQotIENoYW5nZSAiZGF0ZSIgZnJvbSBjaGFyIHRvIGRhdGV0aW1lCi0gQ2hhbmdlICJ2YWx1ZSIgZnJvbSBkb3VibGUgdG8gZmFjdG9yLiBWYWx1ZSBpbmRpY2F0ZXMgdGhlIHNsZWVwIHN0YXRlOiAxID0gYXNsZWVwLCAyID0gcmVzdGxlc3MsIDMgPSBhd2FrZS4gU2VlOiAKIFtGaXRiaXQgZGF0YSBkaWN0aW9uYXJ5IF0oaHR0cHM6Ly93d3cuZml0YWJhc2UuY29tL3Jlc291cmNlcy9rbm93bGVkZ2UtYmFzZS9leHBvcnRpbmctZGF0YS9kYXRhLWRpY3Rpb25hcmllcy8pCiAtIFJlbW92ZSBkdXBsaWNhdGUgdmFsdWVzOiA1NDMKCgpgYGB7cn0KIyBDbGVhbiBtaW51dGVfc2xlZXAgZGF0YSBzZXQKCm1pbnV0ZV9zbGVlcF9jbGVhbiA8LQogICMgQ2xlYW4gY29sdW1uIG5hbWVzCiAgY2xlYW5fbmFtZXMobWludXRlX3NsZWVwKSAlPiUKICAjIENvcnJlY3QgY29sdW1uIHR5cGVzCiAgbXV0YXRlKHZhbHVlID0gYXMuZmFjdG9yKHZhbHVlKSkgJT4lICMgZnJvbSBkb3VibGUgdG8gY2hyCiAgbXV0YXRlKGlkID0gYXMuY2hhcmFjdGVyKGlkKSkgJT4lICMgZnJvbSBkb3VibGUgdG8gY2hyCiAgbXV0YXRlKGRhdGUgPSBhc19kYXRldGltZShkYXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gIiVtLyVkLyVZICVJOiVNOiVTICVwIikpICU+JSAjIEZyb20gY2hyIHRvIGRhdGV0aW1lCiAgIyBSZW1vdmUgZHVwbGljYXRlIHJvd3MKICBkaXN0aW5jdCgpCgojIENoZWNrIGNsZWFuIGRhaWx5X2FjdGl2aXR5IGRhdGEgc2V0CmdsaW1wc2UobWludXRlX3NsZWVwX2NsZWFuKQoKCiMgQ2hlY2sgbWlzc2luZyB2YWx1ZXMgYW5kIGR1cGxpY2F0ZXMgYWZ0ZXIgY2xlYW5pbmcKY2F0KCJcbiIsCiAgICAiTWlzc2luZyB2YWx1ZXM6IiwKICAgIHN1bShpcy5uYShtaW51dGVfc2xlZXBfY2xlYW4pKSwKICAgICJcbiIsCiAgICAiRHVwbGljYXRlIHZhbHVlczoiLAogICAgc3VtKGR1cGxpY2F0ZWQobWludXRlX3NsZWVwX2NsZWFuKSkpCmBgYAoKCiMjIENsZWFuIHRoZSBzZWNvbmRzX2hlYXJ0cmF0ZSBkYXRhIHNldAoKCmBgYHtyfQojIENoZWNrIHNlY29uZHNfaGVhcnRyYXRlIHNldCBiZWZvcmUgY2xlYW5pbmcKZ2xpbXBzZShzZWNvbmRzX2hlYXJ0cmF0ZSkKCiMgQ2hlY2sgbWlzc2luZyB2YWx1ZXMgYW5kIGR1cGxpY2F0ZXMKY2F0KAogICJcbiIsCiAgIk1pc3NpbmcgdmFsdWVzOiIsIHN1bShpcy5uYShzZWNvbmRzX2hlYXJ0cmF0ZSkpLAogICJcbiIsCiAgIkR1cGxpY2F0ZSB2YWx1ZXM6Iiwgc3VtKGR1cGxpY2F0ZWQoc2Vjb25kc19oZWFydHJhdGUpKQopCmBgYAoKTGV0IHVzIGNsZWFuOgoKLSBDaGFuZ2UgY29sdW1uIG5hbWVzIHRvIGxvd2VyIGNhc2UgYmVjYXVzZSBSIGlzIGNhc2Ugc2Vuc2l0aXZlCi0gQ2hhbmdlICJJZCIgZnJvbSBkb3VibGUgdG8gYSBjaGFyYWN0ZXIgYmVjYXVzZSB0aGUgbnVtYmVyIHJlcHJlc2VudHMgYSBjYXRlZ29yeQotIENoYW5nZSAiVGltZSIgZnJvbSBjaGFyIHRvIGRhdGV0aW1lIGFuZCByZW5hbWUgaXQgZGF0ZV90aW1lCi0gUmVuYW1lICJWYWx1ZSIgdG8gaGVhcnRfcmF0ZQogW0ZpdGJpdCBkYXRhIGRpY3Rpb25hcnkgXShodHRwczovL3d3dy5maXRhYmFzZS5jb20vcmVzb3VyY2VzL2tub3dsZWRnZS1iYXNlL2V4cG9ydGluZy1kYXRhL2RhdGEtZGljdGlvbmFyaWVzLykKCgpgYGB7cn0KIyBDbGVhbiBzZWNvbmRzX2hlYXJ0cmF0ZSBkYXRhIHNldAoKc2Vjb25kc19oZWFydHJhdGVfY2xlYW4gPC0KICAjIENsZWFuIGNvbHVtbiBuYW1lcwogIGNsZWFuX25hbWVzKHNlY29uZHNfaGVhcnRyYXRlKSAlPiUKICAjIENvcnJlY3QgY29sdW1uIHR5cGVzCiAgbXV0YXRlKGlkID0gYXMuY2hhcmFjdGVyKGlkKSkgJT4lICMgZnJvbSBkb3VibGUgdG8gY2hyCiAgbXV0YXRlKHRpbWUgPSBhc19kYXRldGltZSh0aW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gIiVtLyVkLyVZICVJOiVNOiVTICVwIikpICU+JSAjIGZyb20gY2hyIHRvIGRhdGV0aW1lCiAgIyBSZW5hbWUgY29sdW1ucwogIHJlbmFtZShkYXRlX3RpbWUgPSB0aW1lLAogICAgICAgICBoZWFydF9yYXRlID0gdmFsdWUpICU+JQogICMgUmVtb3ZlIGR1cGxpY2F0ZSByb3dzCiAgZGlzdGluY3QoKQoKIyBDaGVjayBjbGVhbiBkYWlseV9hY3Rpdml0eSBkYXRhIHNldApnbGltcHNlKHNlY29uZHNfaGVhcnRyYXRlX2NsZWFuKQoKCiMgQ2hlY2sgbWlzc2luZyB2YWx1ZXMgYW5kIGR1cGxpY2F0ZXMgYWZ0ZXIgY2xlYW5pbmcKCmNhdCgiXG4iLAogICAgIk1pc3NpbmcgdmFsdWVzOiIsCiAgICBzdW0oaXMubmEoc2Vjb25kc19oZWFydHJhdGVfY2xlYW4pKSwKICAgICJcbiIsCiAgICAiRHVwbGljYXRlIHZhbHVlczoiLAogICAgc3VtKGR1cGxpY2F0ZWQoc2Vjb25kc19oZWFydHJhdGVfY2xlYW4pKSkKCgojIGFzX2RhdGV0aW1lKCkgY29udmVydHMgd2l0aCBkZWZhdWx0IHRpbWV6b25lID0gIlVUQyIKYGBgCgoKCiMjIENsZWFuIHRoZSB3ZWlnaHRfbG9ncyBkYXRhIHNldAoKCmBgYHtyfQojIENoZWNrIHdlaWdodF9sb2dzIHNldCBiZWZvcmUgY2xlYW5pbmcKCmdsaW1wc2Uod2VpZ2h0X2xvZ3MpCgojIENoZWNrIG1pc3NpbmcgdmFsdWVzIGFuZCBkdXBsaWNhdGVzCmNhdCgiXG4iLAogICAgIk1pc3NpbmcgdmFsdWVzOiIsCiAgICBzdW0oaXMubmEod2VpZ2h0X2xvZ3MpKSwKICAgICJcbiIsCiAgICAiRHVwbGljYXRlIHZhbHVlczoiLAogICAgc3VtKGR1cGxpY2F0ZWQod2VpZ2h0X2xvZ3MpKSkKYGBgCgoKTGV0IHVzIGNsZWFuOgotIENoYW5nZSBjb2x1bW4gbmFtZXMgdG8gbG93ZXIgY2FzZSBiZWNhdXNlIFIgaXMgY2FzZSBzZW5zaXRpdmUKLSBDaGFuZ2UgIklkIiBmcm9tIGRvdWJsZSB0byBhIGNoYXJhY3RlciBiZWNhdXNlIHRoZSBudW1iZXIgcmVwcmVzZW50cyBhIGNhdGVnb3J5Ci0gQ2hhbmdlICJEYXRlIiBmcm9tIGNoYXIgdG8gZGF0ZXRpbWUgYW5kIHJlbmFtZSBpdCBkYXRlX3RpbWUKLSBDaGFuZ2UgTkEgdG8gMCBpbiB0aGUgY29sdW1uICJmYXQiCgoKYGBge3J9CiMgQ2xlYW4gIHdlaWdodF9sb2dzIGRhdGEgc2V0Cgp3ZWlnaHRfbG9nc19jbGVhbiA8LQogICMgQ2xlYW4gY29sdW1uIG5hbWVzCiAgY2xlYW5fbmFtZXMod2VpZ2h0X2xvZ3MpICU+JQogICMgQ29ycmVjdCBjb2x1bW4gdHlwZXMKICBtdXRhdGUoaWQgPSBhcy5jaGFyYWN0ZXIoaWQpKSAlPiUgIyBmcm9tIGRvdWJsZSB0byBjaHIKICBtdXRhdGUoZGF0ZSA9IGFzX2RhdGV0aW1lKGRhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAiJW0vJWQvJVkgJUk6JU06JVMgJXAiKSkgJT4lICMgZnJvbSBjaHIgdG8gZGF0ZXRpbWUKICAjIFJlbmFtZSBjb2x1bW5zCiAgcmVuYW1lKGRhdGVfdGltZSA9IGRhdGUpICU+JQogICMgUmVtb3ZlIGR1cGxpY2F0ZSByb3dzCiAgZGlzdGluY3QoKQoKCgojIENoYW5nZSBOQSB0byAwIGluIHRoZSBjb2x1bW4gImZhdCIKd2VpZ2h0X2xvZ3NfY2xlYW4kZmF0W2lzLm5hKHdlaWdodF9sb2dzX2NsZWFuJGZhdCldIDwtIDAKCiMgQ2hlY2sgY2xlYW4gZGFpbHlfYWN0aXZpdHkgZGF0YSBzZXQKZ2xpbXBzZSh3ZWlnaHRfbG9nc19jbGVhbikKCgoKIyBDaGVjayBtaXNzaW5nIHZhbHVlcyBhbmQgZHVwbGljYXRlcyBhZnRlciBjbGVhbmluZwoKY2F0KCJcbiIsCiAgICAiTWlzc2luZyB2YWx1ZXM6IiwKICAgIHN1bShpcy5uYSh3ZWlnaHRfbG9nc19jbGVhbikpLAogICAgIlxuIiwKICAgICJEdXBsaWNhdGUgdmFsdWVzOiIsCiAgICBzdW0oZHVwbGljYXRlZCh3ZWlnaHRfbG9nc19jbGVhbikpKQoKCmBgYAoKCgojIyBEaXN0cmlidXRpb24gb2YgaWRzIGFjcm9zcyBkYXRhIHNldHMKCgpgYGB7cn0KIyBMb29wIHRocm91Z2ggZWFjaCAgZGF0YSBzZXQgYW5kIHByaW50IHRoIG51bWJlciBvIGZ1bmlxdSBpZHMKZGF0YXNldHMgPC0gYygKICAgICJkYWlseV9hY3Rpdml0eV9jbGVhbiIsCiAgICAiZGFpbHlfc2xlZXBfY2xlYW4iLAogICAgImhvdXJseV9hY3Rpdml0eV9jbGVhbiIsCiAgICAibWludXRlX3NsZWVwX2NsZWFuIiwKICAgICJzZWNvbmRzX2hlYXJ0cmF0ZV9jbGVhbiIsCiAgICAid2VpZ2h0X2xvZ3NfY2xlYW4iCikKCgpyZXN1bHRzX2RmIDwtIGRhdGEuZnJhbWUoRGF0YXNldCA9IGNoYXJhY3RlcigwKSwgZGlzdGluY3RfSURzID0gaW50ZWdlcigwKSkKCmZvciAoZGF0YXNldF9uYW1lIGluIGRhdGFzZXRzKSB7CiAgICBkYXRhc2V0IDwtIGdldChkYXRhc2V0X25hbWUpICAjIFJldHJpZXZlIHRoZSBkYXRhc2V0IGJ5IGl0cyBuYW1lCiAgICBkaXN0aW5jdF9pZHMgPC0gbGVuZ3RoKHVuaXF1ZShkYXRhc2V0JGlkKSkgICMgQ2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgZGlzdGluY3QgSURzCiAgICAKICAgIHJlc3VsdF9yb3cgPC0gZGF0YS5mcmFtZShEYXRhc2V0ID0gZGF0YXNldF9uYW1lLCBkaXN0aW5jdF9JRHMgPSBkaXN0aW5jdF9pZHMpCiAgICByZXN1bHRzX2RmIDwtIGJpbmRfcm93cyhyZXN1bHRzX2RmLCByZXN1bHRfcm93KQp9Cgpzb3J0ZWRfcmVzdWx0cyA8LSByZXN1bHRzX2RmICU+JSBhcnJhbmdlKC0gZGlzdGluY3RfSURzICkKCnByaW50KHNvcnRlZF9yZXN1bHRzKQoKYGBgCkRpZmZlcmVuY2VzIGluIHRoZSBudW1iZXIgb2YgdW5pcXVlIElEcyBiZXR3ZWVuIHRoZSBkYXRhIHNldHMgY2FuIGltcGx5IGRpc2NyZXBhbmNpZXMgaW4gZGF0YSBjb2xsZWN0aW9uIG1ldGhvZHMsIGRhdGEgaW5jb21wbGV0ZW5lc3MsIG9yIGRpZmZlcmluZyBsZXZlbHMgb2YgdXNlciBlbmdhZ2VtZW50LgoKCgojIEV4cG9ydCBjbGVhbiBkYXRhIHNldHMKCmBgYHtyfQojIFRvIHVuY29tbWVudCB0aGUgZm9sbG93aW5nIGNvZGUsIHNlbGVjdCBhbGwgdGhlIGxpbmVzIGFuZCBwcmVzcyBzaGlmdCArIGNvbnRyb2wgKyBjIG9uIE1hYwoKCiMgd3JpdGUuY3N2KGRhaWx5X2FjdGl2aXR5X2NsZWFuLCAKIyAgICAgICAgICAgImRhaWx5X2FjdGl2aXR5X2NsZWFuLmNzdiIsIAojICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkKIyAKIyB3cml0ZS5jc3YoZGFpbHlfc2xlZXBfY2xlYW4sIAojICAgICAgICAgICAiZGFpbHlfc2xlZXBfY2xlYW4uY3N2IiwgCiMgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQojIAojIHdyaXRlLmNzdihkYWlseV9zbGVlcF9jbGVhbiwgCiMgICAgICAgICAgICJob3VybHlfYWN0aXZpdHlfY2xlYW4uY3N2IiwgCiMgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQojIAojIHdyaXRlLmNzdihtaW51dGVfc2xlZXBfY2xlYW4sIAojICAgICAgICAgICAibWludXRlX3NsZWVwX2NsZWFuLmNzdiIsCiMgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQojIAojIHdyaXRlLmNzdihzZWNvbmRzX2hlYXJ0cmF0ZV9jbGVhbiwKIyAgICAgICAgICAgInNlY29uZHNfaGVhcnRyYXRlX2NsZWFuLmNzdiIsCiMgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQojIAojIHdyaXRlLmNzdih3ZWlnaHRfbG9nc19jbGVhbiAsIAojICAgICAgICAgICAid2VpZ2h0X2xvZ3NfY2xlYW4gLmNzdiIsCiMgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQoKYGBgCgojIEFuYWx5emUgUGhhc2U6IEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMKCgoKIyMgIEVEQSBmb3IgZGFpbHlfYWN0aXZpdHlfY2xlYW4KYGBge3J9CnN0cihkYWlseV9hY3Rpdml0eV9jbGVhbikKYGBgCiMjIyBVbml2YXJpYXRlIGFuYWx5c2lzIGZvciBkYWlseV9hY3Rpdml0eV9jbGVhbgoKCiMjIyBOdW1lcmljYWwgdmFyaWFibGVzCmBgYHtyfQojIFN1YnNldCBudW1lcmljIGNvbHVtbnMgCm51bV9kZiA8LSBzZWxlY3RfaWYoZGFpbHlfYWN0aXZpdHlfY2xlYW4sIGlzLm51bWVyaWMpCgojIElkZW50aWZ5IG51bWVyaWMgY29sdW1ucwpjb2xuYW1lcyhudW1fZGYpCgoKYGBgCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcid9CgojIHBsb3R0aW5nIGFsbCBudW1lcmljYWwgdmFyaWFibGVzCmNvbF9uYW1lcyA8LSBjb2xuYW1lcyhudW1fZGYpCmZvciAoaSBpbiBjb2xfbmFtZXMpIHsKICBzdXBwcmVzc1dhcm5pbmdzKHByaW50KAogICAgZ2dwbG90KG51bV9kZiwgYWVzKG51bV9kZltbaV1dKSkgKwogICAgICBnZW9tX2hpc3RvZ3JhbSgKICAgICAgICBiaW5zID0gMzAsCiAgICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICAgIGZpbGwgPSAiZ3JheSIsCiAgICAgICAgYWVzKHkgPSAuLmRlbnNpdHkuLikKICAgICAgKSArCiAgICAgIGdlb21fZGVuc2l0eSgKICAgICAgICBjb2xvciA9ICJibHVlIiwKICAgICAgICBzaXplID0gMQogICAgICApICsKICAgICAgeGxhYihpKSArIHlsYWIoIkNvdW50IikgKwogICAgICBnZ3RpdGxlKHBhc3RlKCJIaXN0b2dyYW0gYW5kIERlbnNpdHkgUGxvdCBvZiIsIGkpKQogICkpCn0KCgoKCmBgYAoKCgoKT2JzZXJ2YXRpb25zOgoKLSBNYW55IHZhcmlhYmxlcyBzaG93IGEgcmlnaHQtc2tld2VkIGRpc3RyaWJ1dGlvbjogYSBsYXJnZXIgbnVtYmVyIG9mIGRhdGEgdmFsdWVzIGFyZSBsb2NhdGVkIG9uIHRoZSBsZWZ0IHNpZGUgb2YgdGhlIGN1cnZlCgotIFRoZSB2YXJpYWJsZXMgdG90YWxfc3RlcHMsIHRvdGFsX2Rpc3RhbmNlLCB0cmFja2VyX2Rpc3RhbmNlIGhhdmUgYSBzaW1pbGFyIGRpc3RyaWJ1dGlvbi4gV2UgY2FuIGV4cGxvcmUgdGhlaXIgY29ycmVsYXRpb25zIGxhdGVyCgotIFNpbmNlIHRoZSBkaXN0cmlidXRpb25zIGFyZSBub3Qgbm9ybWFsLiBUaGUgbWVkaWFuIGlzIGEgYmV0dGVyIGluZGljYXRvciBvZiBjZW50cmFsIHRlbmRlbmN5IGZvciB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlcyBpbiB0aGVzZSBkYXRhIHNldAoKLSAqKlRoZSB2YXJpYWJsZSBsb2dnZWRfYWN0aXZpdGllc19kaXN0YW5jZSBhbmQgc2VkZW50YXJ5X2FjdGl2ZV9kaXN0YW5jZSBtaWdodCBub3QgcHJvdmlkZSB1c2VmdWwgaW5mb3JtYXRpb24gc2luY2UgbW9zdCBvZiB0aGUgZGF0YSBwb2ludHMgYXJlIHplcm8uIEl0IHNlZW1zIHRoYXQgdGhlIHVzZXJzIGFyZSBub3QgbG9nZ2luZyB0aGUgZGlzdGFuY2UgZnJlcXVlbnRseSoqCgotICBUaGUgZm9sbG93aW5nIHZhcmlhYmxlcyBzZWVtIHJlbGF0ZWQuIFdlIHdpbGwgZXhwbG9yZSB0aGVtIGZ1cnRoZXIgaW4gdGhlIGJpdmFyaWF0ZSBhbmFseXNpcyBzZWN0aW9uOgoKc2VkZW50YXJ5X21pbnV0ZXM7IHNlZGVudGFyeV9hY3RpdmVfZGlzdGFuY2UgCmxpZ2h0bHlfYWN0aXZlX21pbnV0ZXM7IGxpZ2h0X2FjdGl2ZV9kaXN0YW5jZSAgCmZhaXJseV9hY3RpdmVfbWludXRlczsgbW9kZXJhdGVseV9hY3RpdmVfZGlzdGFuY2UKdmVyeV9hY3RpdmVfbWludXRlczsgdmVyeV9hY3RpdmVfZGlzdGFuY2UgICAKCi0gVGhlIHZhcmlhYmxlcyBjYWxvcmllcyBhbmQgc2VkZW50YXJ5X21pbnV0ZXMgZXhoaWJpdCBhIG11bHRpbW9kYWwgZGlzdHJpYnV0aW9uLCBpbmRpY2F0aW5nIHRoZSBwcmVzZW5jZSBvZiBzdWJwb3B1bGF0aW9ucyB3aXRoaW4gdGhlIGRhdGEuIEluIHRoaXMgZGF0YXNldCwgZ2VuZGVyIGNvdWxkIGJlIGEgcG90ZW50aWFsIHZhcmlhYmxlIHRoYXQgd291bGQgcmVzdWx0IGluIGEgYmltb2RhbCBkaXN0cmlidXRpb24gd2hlbiBleGFtaW5pbmcgaGlzdG9ncmFtcyBvZiBjYWxvcmllcyBhbmQgc2VkZW50YXJ5IG1pbnV0ZXMuIFVuZm9ydHVuYXRlbHksIHRoZSBnZW5kZXIgb2YgdGhlIHVzZXJzIGlzIG5vdCBwcm92aWRlZCwgbGltaXRpbmcgb3VyIGFiaWxpdHkgdG8gY29uZmlybSB0aGlzIGh5cG90aGVzaXMuCgoKCgojIyMgQ2F0ZWdvcmljYWwgdmFyaWFibGVzCgpgYGB7cn0KIyBTdWJzZXQgbnVtZXJpYyBjb2x1bW5zIAoKc2VsZWN0X2lmKGRhaWx5X2FjdGl2aXR5X2NsZWFuLCBuZWdhdGUoaXMubnVtZXJpYykpCgpgYGAKCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcid9CiMgQ2hlY2sgY291bnRzIGJ5IGlkCmdncGxvdChkYXRhPWRhaWx5X2FjdGl2aXR5X2NsZWFuKSArIAogIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMgKHg9IHJlb3JkZXIoaWQsIGlkLGxlbmd0aCkpKSsKICB4bGFiKCJpZCIpICsKICBjb29yZF9mbGlwKCkKICAKI2h0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS85MjMxODU3LzE1MzMzNTgwCgojcmVvcmRlcihpZCwgaWQsIGxlbmd0aCkgdGFrZXMgdGhlIGlkIHZhcmlhYmxlLCB1c2VzIGl0c2VsZiB0byBkZXRlcm1pbmUgdGhlIG9yZGVyLCBhbmQgdXNlcyB0aGUgbGVuZ3RoKCkgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSB2YWx1ZXMgdXNlZCBmb3Igb3JkZXJpbmcuIEVzc2VudGlhbGx5LCB0aGlzIHJlb3JkZXJzIHRoZSBsZXZlbHMgb2YgdGhlIGlkIHZhcmlhYmxlIGJhc2VkIG9uIHRoZSBsZW5ndGggb2YgdGhlaXIgbmFtZXMuCmBgYAoKCmBgYHtyfQpjb3VudF9tYXhfcmF0aW8gPC0gZGFpbHlfYWN0aXZpdHlfY2xlYW4gJT4lCiAgY291bnQoaWQpICU+JQogIHJlbmFtZShpZCA9ICJpZCIsIGNvdW50ID0gIm4iKSAlPiUKICBtdXRhdGUocGVyY2VudF9vZl9tYXggPSBjb3VudCAvIG1heChjb3VudCkgKiAxMDApICU+JQogIGFycmFuZ2UoZGVzYyhwZXJjZW50X29mX21heCkpCgoKYGBgCgoKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30KCiMgQ3JlYXRlIGJhciBncmFwaCB3aXRoIHBlcmNlbnRhZ2Ugb2YgZW50cmllcyBjb21wYXJlZCB0byBtYXhpbXVtCmdncGxvdChjb3VudF9tYXhfcmF0aW8sIGFlcyh4ID0gcmVvcmRlcihpZCwgcGVyY2VudF9vZl9tYXgpLCB5ID0gcGVyY2VudF9vZl9tYXgpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICB4bGFiKCJJRCIpICsKICB5bGFiKCJQZXJjZW50YWdlIG9mIE1heGltdW0gQ291bnQiKSArCiAgZ2d0aXRsZSgiQ291bnQgYnkgSUQgYW5kIFBlcmNlbnRhZ2Ugb2YgTWF4aW11bSBDb3VudCIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD01MCwgY29sb3I9Im9yYW5nZSIsIGxpbmV3aWR0aD0xKSsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9NzUsIGNvbG9yPSJyZWQiLCBsaW5ld2lkdGg9MSkrCiAgY29vcmRfZmxpcCgpCgoKYGBgCgpgYGB7cn0KIyBwZXJjZW50X29mX21heCA+IDc1JQoKcGVyY2VudF9vZl9tYXhfdG9wXzc1IDwtIGZpbHRlcihjb3VudF9tYXhfcmF0aW8sIHBlcmNlbnRfb2ZfbWF4ID49NzUpCnBlcmNlbnRfb2ZfbWF4X3RvcF83NSAKYGBgCgpgYGB7cn0KIyBwZXJjZW50X29mX21heCA8IDc1CgpwZXJjZW50X29mX21heF91bmRlcl83NSA8LSBmaWx0ZXIoY291bnRfbWF4X3JhdGlvLCBwZXJjZW50X29mX21heCA8IDc1KQpwZXJjZW50X29mX21heF91bmRlcl83NSAKYGBgCgoKCmBgYHtyfQpkYWlseV9hY3Rpdml0eV9jbGVhbiRhY3Rpdml0eV9kYXRlICU+JSBzdW1tYXJ5KCkKYGBgCgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInIH0KZ2dwbG90KGRhdGE9ZGFpbHlfYWN0aXZpdHlfY2xlYW4gLCBhZXMoeCA9IGFjdGl2aXR5X2RhdGUpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImxpZ2h0Ymx1ZSIpICsKICBsYWJzKHggPSAiQWN0aXZpdHkgRGF0ZSIsIHkgPSAiRnJlcXVlbmN5IiwgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIEFjdGl2aXR5IERhdGUiKSAKCmBgYAoKCgpPYnNlcnZhdGlvbnM6CgotIEl0IGFwcGVhcnMgdGhhdCB0aGVyZSBpcyBtaXNzaW5nIGFjdGl2aXR5IGRhdGEgdG93YXJkcyB0aGUgZW5kIG9mIHRoZSBhdmFpbGFibGUgcGVyaW9kLCBzcGVjaWZpY2FsbHkgaW4gdGhlIGJlZ2lubmluZyBvZiBNYXkKCgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInIH0KIyBJbnZlc3RpZ2F0ZSBpZiB0aGUgbWlzc2luZyBhY3Rpdml0eSBkYXRhIGNvaW5jaWRlcyB3aXRoIHRoZSBhYnNlbmNlIG9mIGVudHJpZXMgZm9yIGNlcnRhaW4gdXNlciBJRHMuCgpnZ3Bsb3QoZGF0YT1zdWJzZXQoZGFpbHlfYWN0aXZpdHlfY2xlYW4sIGlkICVpbiUgcGVyY2VudF9vZl9tYXhfdG9wXzc1JGlkKSwgYWVzKHggPSBhY3Rpdml0eV9kYXRlKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJsaWdodGJsdWUiKSArCiAgbGFicyh4ID0gIkFjdGl2aXR5IERhdGUiLCB5ID0gIkZyZXF1ZW5jeSIsIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBBY3Rpdml0eSBEYXRlIEZvciBJRHMgd2l0aCBBYm92ZSA3NSUgb2YgRW50cmllcyIpCmBgYApgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicgfQoKZ2dwbG90KGRhdGE9c3Vic2V0KGRhaWx5X2FjdGl2aXR5X2NsZWFuLCBpZCAlaW4lIHBlcmNlbnRfb2ZfbWF4X3VuZGVyXzc1JGlkKSwgYWVzKHggPSBhY3Rpdml0eV9kYXRlKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJsaWdodGJsdWUiKSArCiAgbGFicyh4ID0gIkFjdGl2aXR5IERhdGUiLCB5ID0gIkZyZXF1ZW5jeSIsIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBBY3Rpdml0eSBEYXRlIEZvciBJRHMgd2l0aCB1bmRlciA3NSUgb2YgRW50cmllcyIpCmBgYAotIFVzZXJzIHdpdGggbW9yZSB0aGFuIDc1JSBvZiBkYXRhIGNvbnNpc3RlbnRseSByZXBvcnQgYWN0aXZpdHkgZGF0ZXMsIHdoaWxlIHRob3NlIHdpdGggbGVzcyB0aGFuIDc1JSBvZiBkYXRhIHNob3cgYSBkZWNsaW5lIGluIHJlcG9ydGluZyBzdGFydGluZyBmcm9tIHRoZSBlbmQgb2YgQXByaWwuIFRoZSBkZWNsaW5lIGluIEFjdGl2aXR5IERhdGUgc2VlbXMgdG8gYmUgcHJpbWFyaWx5IGR1ZSB0byBhIGxhY2sgb2YgZGF0YSByZXBvcnRpbmcgZnJvbSBzb21lIHVzZXJzIGR1cmluZyB0aGF0IHBlcmlvZC4KCgoKIyMjIEJpdmFyaWF0ZSBhbmFseXNpcwoKCiMjIyMgQ29ycmVsYXRpb24gYmV0d2VlbiBudW1lcmljYWwgdmFyaWFibGVzCgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpjb3JyIDwtIGNvcihzZWxlY3RfaWYoZGFpbHlfYWN0aXZpdHlfY2xlYW4sIGlzLm51bWVyaWMpKQoKZ2djb3JycGxvdChjb3JyLAogICAgICAgICAgIGhjLm9yZGVyID0gVFJVRSwKICAgICAgICAgICB0eXBlID0gImxvd2VyIiwKICAgICAgICAgICBsYWIgPSBUUlVFLAogICAgICAgICAgIGNvbG9ycyA9IGMoImZpcmVicmljayIsICJ3aGl0ZSIsICJyb3lhbGJsdWUiKSwKICAgICAgICAgICBsYWJfc2l6ZSA9IDQsCiAgICAgICAgICAgbGFiX2NvbCA9ICJibGFjayIsCiAgICAgICAgICAgdGl0bGUgPSAiQ29ycmVsYXRpb24gQmV0d2VlbiBOdW1lcmljYWwgVmFyaWFibGVzIikKCiNodHRwczovL3JkcnIuaW8vZ2l0aHViL21pY3JvcmVzZWFyY2hlci9NaWNyb1Zpcy9tYW4vZ2djb3JycGxvdC5odG1sCmBgYApzZWRlbnRhcnlfbWludXRlczsgc2VkZW50YXJ5X2FjdGl2ZV9kaXN0YW5jZSAKbGlnaHRseV9hY3RpdmVfbWludXRlczsgbGlnaHRfYWN0aXZlX2Rpc3RhbmNlICAKZmFpcmx5X2FjdGl2ZV9taW51dGVzOyBtb2RlcmF0ZWx5X2FjdGl2ZV9kaXN0YW5jZQp2ZXJ5X2FjdGl2ZV9taW51dGVzOyB2ZXJ5X2FjdGl2ZV9kaXN0YW5jZSAgIAoKCgoKYGBge3J9CiMgQ29tcHV0ZSBjb3JyZWxhdGlvbiBtYXRyaXgKY29ycl9tYXRyaXggPC0gY29ycgoKIyBTZXQgdGhlIHRocmVzaG9sZCBmb3IgY29ycmVsYXRpb24KdGhyZXNob2xkIDwtIDAuNjAKCiMgRmluZCBwYWlycyBvZiBoaWdobHkgY29ycmVsYXRlZCB2YXJpYWJsZXMKaGlnaF9jb3JfcGFpcnMgPC0gd2hpY2goYWJzKGNvcnJfbWF0cml4KSA+IHRocmVzaG9sZCAmIGxvd2VyLnRyaShjb3JyX21hdHJpeCwgZGlhZyA9IEZBTFNFKSwgYXJyLmluZCA9IFRSVUUpCgojIEV4dHJhY3QgdGhlIHZhcmlhYmxlIG5hbWVzIGFuZCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgZm9yIHRoZSBjb3JyZWxhdGVkIHBhaXJzCnZhcmlhYmxlX25hbWVzIDwtIGNvbG5hbWVzKGNvcnJfbWF0cml4KQpjb3JfdmFsdWVzIDwtIGFzLnZlY3Rvcihjb3JyX21hdHJpeFtoaWdoX2Nvcl9wYWlyc10pCgojIENyZWF0ZSBhIGRhdGEgZnJhbWUgdG8gc3RvcmUgdGhlIGNvcnJlbGF0ZWQgcGFpcnMgYW5kIHRoZWlyIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cwpjb3JfZGF0YSA8LSBkYXRhLmZyYW1lKAogIFZhcmlhYmxlMSA9IHZhcmlhYmxlX25hbWVzW2hpZ2hfY29yX3BhaXJzWywgMV1dLAogIFZhcmlhYmxlMiA9IHZhcmlhYmxlX25hbWVzW2hpZ2hfY29yX3BhaXJzWywgMl1dLAogIENvcnJlbGF0aW9uID0gY29yX3ZhbHVlcwopCgojIFNvcnQgdGhlIGNvcnJlbGF0ZWQgcGFpcnMgYnkgY29ycmVsYXRpb24gY29lZmZpY2llbnQgaW4gZGVzY2VuZGluZyBvcmRlcgpzb3J0ZWRfY29yX2RhdGEgPC0gY29yX2RhdGFbb3JkZXIoLWNvcl9kYXRhJENvcnJlbGF0aW9uKSwgXQoKIyBSZW1vdmUgdGhlIGluZGV4CnJvdy5uYW1lcyhzb3J0ZWRfY29yX2RhdGEpIDwtIE5VTEwKCiMgRGlzcGxheSB0aGUgc29ydGVkIGNvcnJlbGF0ZWQgdmFyaWFibGUgcGFpcnMgaW4gdGhlIGRhdGFmcmFtZQpwcmludChzb3J0ZWRfY29yX2RhdGEpCgpgYGAKCgotIFRvdGFsX2Rpc3RhbmNlLCB0cmFja2VyX2Rpc3RhbmNlLCBhbmQgdG90YWwgc3RlcHMgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLCBzbyB3ZSB3aWxsIHJldGFpbiBvbmx5IHRvdGFsIGRpc3RhbmNlIGFuZCB0b3RhbCBzdGVwcyBhcyB0aGV5IHByb3ZpZGUgc2ltaWxhciBpbmZvcm1hdGlvbi4KCi0gVGhlIGZvbGxvd2luZyBtaW51dGUgYW5kIGRpc3RhbmNlIHR5cGVzIGFyZSBjb3JyZWxhdGVkLiBXaGljaCBpbmRpY2F0ZXMgdGhhdCB0aGV5IHJlcG9ydCBkaWZmZXJlbnQgYXNwZWN0cyBvZiB0aGUgc2FtZSBhY3Rpdml0eSwgdGhpcyBpcyB0aW1lIG9yIGRpc3RhbmNlOgogIC0gbGlnaHRseV9hY3RpdmVfbWludXRlcyBhbmQgbGlnaHRfYWN0aXZlX2Rpc3RhbmNlIChjb3JyID0gMC44NSkKICAtIGZhaXJseV9hY3RpdmVfbWludXRlcyBhbmQgbW9kZXJhdGVseV9hY3RpdmVfZGlzdGFuY2UgKGNvcnIgPSAwLjk0KQogIC0gdmVyeV9hY3RpdmVfbWludXRlcwlhbmQgdmVyeV9hY3RpdmVfZGlzdGFuY2UgKGNvcnIgPSAwLjgyKQoKLSBUaGVyZSBpcyBhIG1vZGVyYXRlbHkgaGlnaCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB0aW1lIHNwZW50IGR1cmluZyB2ZXJ5IGFjdGl2ZSBwZXJpb2RzIGFuZCB0aGUgdG90YWwgbnVtYmVyIG9mIHN0ZXBzL3RvdGFsIGRpc3RhbmNlOgogIC0gVGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdmVyeV9hY3RpdmVfbWludXRlcyBhbmQgdG90YWxfZGlzdGFuY2UgaXMgMC42OAogIC0gVGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdmVyeV9hY3RpdmVfbWludXRlcyBhbmQgdG90YWxfc3RlcHMgaXMgMC42NgoKLSBUaGVyZSBpcyBhIG1vZGVyYXRlIGNvcnJlbGF0aW9uIG9mIDAuNjEgYmV0d2VlbiB0aGUgdG90YWwgZHVyYXRpb24gb2YgdmVyeSBhY3RpdmUgbWludXRlcyBhbmQgdGhlIGVzdGltYXRlZCBkYWlseSBjYWxvcmllcyBjb25zdW1lZC4KLSBUaGVyZSBpcyBhIG1vZGVyYXRlIGNvcnJlbGF0aW9uIG9mIDAuNjIgYmV0d2VlbiB0aGUgdG90YWwgZGlzdGFuY2UgY292ZXJlZCBhbmQgdGhlIGVzdGltYXRlZCBkYWlseSBjYWxvcmllcyBjb25zdW1lZC4KLSBUaGVyZSBpcyBhIG1vZGVyYXRlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IG9mIDAuNjAgYmV0d2VlbiB0aGUgZGlzdGFuY2UgY292ZXJlZCBkdXJpbmcgbGlnaHQgYWN0aXZpdHkgKGxpZ2h0X2FjdGl2ZV9kaXN0YW5jZSkgYW5kIHRoZSB0b3RhbCBudW1iZXIgb2Ygc3RlcHMgdGFrZW4gKHRvdGFsX3N0ZXBzKS4KCgojIyMjIFNjYXR0ZXJwbG90cyBvZiBzZWxlY3RlZCBoaWdobHkgY29ycmVsYXRlZCB2YXJpYWJsZXMgcGFpcnMgKD4wLjYwKQoKCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcid9CgojIExpc3Qgb2YgY29ycmVsYXRlZCB2YXJpYWJsZSBwYWlycwpjb3JyZWxhdGVkX3BhaXJzIDwtIGxpc3QoYygidG90YWxfc3RlcHMiLCAidG90YWxfZGlzdGFuY2UiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJsaWdodGx5X2FjdGl2ZV9taW51dGVzIiwgImxpZ2h0X2FjdGl2ZV9kaXN0YW5jZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgYygiZmFpcmx5X2FjdGl2ZV9taW51dGVzIiwgIm1vZGVyYXRlbHlfYWN0aXZlX2Rpc3RhbmNlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJ2ZXJ5X2FjdGl2ZV9taW51dGVzIiwgInZlcnlfYWN0aXZlX2Rpc3RhbmNlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJ2ZXJ5X2FjdGl2ZV9taW51dGVzIiwgInRvdGFsX2Rpc3RhbmNlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJ2ZXJ5X2FjdGl2ZV9taW51dGVzIiwgInRvdGFsX3N0ZXBzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJ2ZXJ5X2FjdGl2ZV9taW51dGVzIiwgImNhbG9yaWVzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCJ0b3RhbF9kaXN0YW5jZSIsICJjYWxvcmllcyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgYygibGlnaHRfYWN0aXZlX2Rpc3RhbmNlIiwgInRvdGFsX3N0ZXBzIikpCgojIExvb3Agb3ZlciBlYWNoIHBhaXIgYW5kIGNyZWF0ZSBzY2F0dGVyIHBsb3QKZm9yIChwYWlyIGluIGNvcnJlbGF0ZWRfcGFpcnMpIHsKICB2YXIxIDwtIHBhaXJbMV0KICB2YXIyIDwtIHBhaXJbMl0KICAKICAjIENhbGN1bGF0ZSBhdmVyYWdlcwogIGF2Z192YXIxIDwtIG1lYW4oZGFpbHlfYWN0aXZpdHlfY2xlYW5bW3ZhcjFdXSwgbmEucm0gPSBUUlVFKQogIGF2Z192YXIyIDwtIG1lYW4oZGFpbHlfYWN0aXZpdHlfY2xlYW5bW3ZhcjJdXSwgbmEucm0gPSBUUlVFKQogIAogICMgQ3JlYXRlIHNjYXR0ZXIgcGxvdCB1c2luZyBnZ3Bsb3QyIHdpdGggYWVzKCkKICBwcmludChnZ3Bsb3QoZGF0YSA9IGRhaWx5X2FjdGl2aXR5X2NsZWFuLCBhZXMoeCA9ICEhc3ltKHZhcjEpLCB5ID0gISFzeW0odmFyMikpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYXZnX3ZhcjEsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGF2Z192YXIyLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibHVlIikgKwogICAgeGxhYih2YXIxKSArIHlsYWIodmFyMikgKwogICAgZ2d0aXRsZShwYXN0ZSgiU2NhdHRlciBQbG90IHdpdGggQXZlcmFnZSBSZWZlcmVuY2UgTGluZXMgb2YiLCB2YXIxLCAidnMiLCB2YXIyKSkpCn0KYGBgCgoKIyMjIFVzZXIgQmVoYXZpb3IgZm9yIGRhaWx5IGFjdGl2aXR5IGRhdGFzZXQKCiMjIyMgVG90YWwgc3RlcHM6IFRvdGFsIG51bWJlciBvZiBzdGVwcyB0YWtlbi4KCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJyB9CiMgQ3JlYXRlIGEgYm94cGxvdCBmb3IgdG90YWxfc3RlcHMKYm94cGxvdChkYWlseV9hY3Rpdml0eV9jbGVhbiR0b3RhbF9zdGVwcywgCiAgICAgICAgbWFpbiA9ICJCb3hwbG90IG9mIFRvdGFsIFN0ZXBzIiwKICAgICAgICB5bGFiID0gIlRvdGFsIFN0ZXBzIikKCiMgQ2FsY3VsYXRlIHRoZSBtZWRpYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbgptZWRpYW5fdmFsdWUgPC0gbWVkaWFuKGRhaWx5X2FjdGl2aXR5X2NsZWFuJHRvdGFsX3N0ZXBzKQpzdGRfZGV2IDwtIHJvdW5kKHNkKGRhaWx5X2FjdGl2aXR5X2NsZWFuJHRvdGFsX3N0ZXBzKSwyKQoKIyBJZGVudGlmeSBvdXRsaWVycwpvdXRsaWVycyA8LSBib3hwbG90LnN0YXRzKGRhaWx5X2FjdGl2aXR5X2NsZWFuJHRvdGFsX3N0ZXBzKSRvdXQKCiMgQ291bnQgdGhlIG51bWJlciBvZiBvdXRsaWVycwpudW1fb3V0bGllcnMgPC0gbGVuZ3RoKG91dGxpZXJzKQoKIyBDcmVhdGUgdGhlIGxlZ2VuZCBsYWJlbCB3aXRoIG1lZGlhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgb3V0bGllciBjb3VudApsZWdlbmRfbGFiZWwgPC0gcGFzdGUoIk1lZGlhbjoiLCBtZWRpYW5fdmFsdWUsIAogICAgICAgICAgICAgICAgICAgICAgIlxuU3RhbmRhcmQgRGV2aWF0aW9uOiIsIHN0ZF9kZXYsIAogICAgICAgICAgICAgICAgICAgICAgIlxuT3V0bGllcnM6IiwgbnVtX291dGxpZXJzKQoKIyBBZGQgdGhlIGxlZ2VuZCB3aXRoIG1lZGlhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgb3V0bGllciBjb3VudApsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gbGVnZW5kX2xhYmVsLCBwY2ggPSAiIiwgY29sID0gImJsYWNrIiwgYnR5ID0gIm4iLCBjZXggPSAwLjg1KQoKYGBgCgpgYGB7cn0KCiMgU3RlcHMgYXZlcmFnZXMgYnkgSURzCnN0ZXBzX2RmIDwtIGRhaWx5X2FjdGl2aXR5X2NsZWFuICU+JQogIGdyb3VwX2J5KGlkKSAlPiUKICBzdW1tYXJpc2UoYXZlcmFnZV9zdGVwcyA9IG1lYW4odG90YWxfc3RlcHMpLCBtZWRpYW5fc3RlcHMgPW1lZGlhbih0b3RhbF9zdGVwcyksIG4gPSBuKCkpCgpzdGVwc19kZgpgYGAKCmBgYHtyfQojIENhbGN1bGF0ZSBwZXJjZW50YWdlcyBmb3IgdGhlIGF2ZXJhZ2UgY29sdW1uCmF0X2xlYXN0XzEwa19hdmcgPC0gc3VtKHN0ZXBzX2RmJGF2ZXJhZ2Vfc3RlcHMgPj0gMTAwMDApIC8gbnJvdyhzdGVwc19kZikgKiAxMDAKYmV0d2Vlbl81S18xMEtfYXZnIDwtIHN1bShzdGVwc19kZiRhdmVyYWdlX3N0ZXBzID49IDUwMDAgJiBzdGVwc19kZiRhdmVyYWdlX3N0ZXBzIDwgMTAwMDApIC8gbnJvdyhzdGVwc19kZikgKiAxMDAKYmVsb3dfNWtfYXZnIDwtIHN1bShzdGVwc19kZiRhdmVyYWdlX3N0ZXBzIDwgNTAwMCkgLyBucm93KHN0ZXBzX2RmKSAqIDEwMAoKIyBDYWxjdWxhdGUgcGVyY2VudGFnZXMgZm9yIHRoZSBtZWRpYW4gY29sdW1uCmF0X2xlYXN0XzEwa19tZWQgPC0gc3VtKHN0ZXBzX2RmJG1lZGlhbl9zdGVwcyA+PSAxMDAwMCkgLyBucm93KHN0ZXBzX2RmKSAqIDEwMApiZXR3ZWVuXzVLXzEwS19tZWQgPC0gc3VtKHN0ZXBzX2RmJG1lZGlhbl9zdGVwcyA+PSA1MDAwICYgc3RlcHNfZGYkbWVkaWFuX3N0ZXBzIDwgMTAwMDApIC8gbnJvdyhzdGVwc19kZikgKiAxMDAKYmVsb3dfNWtfbWVkIDwtIHN1bShzdGVwc19kZiRtZWRpYW5fc3RlcHMgPCA1MDAwKSAvIG5yb3coc3RlcHNfZGYpICogMTAwCgojIENyZWF0ZSBhIGRhdGEgZnJhbWUgZm9yIHRoZSBzdGVwcyBjYXRlZ29yaWVzCnBlcmNlbnRhZ2Vfc3RlcHNfZGY8LSBkYXRhLmZyYW1lKAogIENhdGVnb3J5ID0gYygiQmVsb3cgNSwwMDAiLCAiQmV0d2VlbiA1LDAwMCBhbmQgMTAsMDAwIiwgIkF0IGxlYXN0IDEwLDAwMCIpLAogIFBlcmNlbnRhZ2VfQXZlcmFnZSA9IHJvdW5kKGMoYmVsb3dfNWtfYXZnLCBiZXR3ZWVuXzVLXzEwS19hdmcsIGF0X2xlYXN0XzEwa19hdmcpKSwKICBQZXJjZW50YWdlX01lZGlhbiA9IHJvdW5kKGMoYmVsb3dfNWtfbWVkLCBiZXR3ZWVuXzVLXzEwS19tZWQsIGF0X2xlYXN0XzEwa19tZWQpKSkKcGVyY2VudGFnZV9zdGVwc19kZgoKYGBgCgoKCgoKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJyB9CiMgQ29udmVydCBDYXRlZ29yeSB0byBhIGZhY3RvciB3aXRoIGN1c3RvbSBmYWN0b3IgbGV2ZWxzCnBlcmNlbnRhZ2Vfc3RlcHNfZGYkQ2F0ZWdvcnkgPC0gZmFjdG9yKHBlcmNlbnRhZ2Vfc3RlcHNfZGYkQ2F0ZWdvcnksIGxldmVscyA9IGMoIkJlbG93IDUsMDAwIiwgIkJldHdlZW4gNSwwMDAgYW5kIDEwLDAwMCIsICJBdCBsZWFzdCAxMCwwMDAiKSkKCiMgQ3JlYXRlIGEgYmFyIHBsb3QgdXNpbmcgZ2dwbG90CmdncGxvdChwZXJjZW50YWdlX3N0ZXBzX2RmLCBhZXMoeCA9IENhdGVnb3J5LCB5ID0gUGVyY2VudGFnZV9BdmVyYWdlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImJsdWUiKSArCiAgbGFicyh4ID0gIkF2ZXJhZ2UgVG90YWwgU3RlcHMiLCB5ID0gIlBlcmNlbnRhZ2Ugb2YgVXNlcnMiLCB0aXRsZSA9ICI1OCUgb2YgVXNlcnMgQXZlcmFnZSA1LDAwMC0xMCwwMDAgU3RlcCBEYWlseSIsc3VidGl0bGUgPSAiT25seSAyMSUgQWNoaWV2ZSB0aGUgMTAsMDAwLVN0ZXAgR29hbCIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKFBlcmNlbnRhZ2VfQXZlcmFnZSwgIiUiKSksIHZqdXN0ID0gLTAuNSwgY29sb3IgPSAiYmxhY2siKSArIAogIHlsaW0oMCwgMTAwKSArICB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKQoKCmBgYAoKCiMjIyMgVG90YWwgRGlzdGFuY2U6IFRvdGFsIGtpbG9tZXRlcnMgdHJhY2tlZC4KCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJyB9CiMgQ3JlYXRlIGEgYm94cGxvdCBmb3IgdG90YWxfZGlzdGFuY2UKYm94cGxvdChkYWlseV9hY3Rpdml0eV9jbGVhbiR0b3RhbF9kaXN0YW5jZSwgCiAgICAgICAgbWFpbiA9ICJCb3hwbG90IG9mIFRvdGFsIERpc3RhbmNlIiwKICAgICAgICB5bGFiID0gIlRvdGFsIERpc3RhbmNlIikKCiMgQ2FsY3VsYXRlIHRoZSBtZWRpYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbgptZWRpYW5fdmFsdWUgPC0gbWVkaWFuKGRhaWx5X2FjdGl2aXR5X2NsZWFuJHRvdGFsX2Rpc3RhbmNlKQpzdGRfZGV2IDwtIHNkKGRhaWx5X2FjdGl2aXR5X2NsZWFuJHRvdGFsX2Rpc3RhbmNlKQoKIyBJZGVudGlmeSBvdXRsaWVycwpvdXRsaWVycyA8LSBib3hwbG90LnN0YXRzKGRhaWx5X2FjdGl2aXR5X2NsZWFuJHRvdGFsX2Rpc3RhbmNlKSRvdXQKCiMgQ291bnQgdGhlIG51bWJlciBvZiBvdXRsaWVycwpudW1fb3V0bGllcnMgPC0gbGVuZ3RoKG91dGxpZXJzKQoKIyBDcmVhdGUgdGhlIGxlZ2VuZCBsYWJlbCB3aXRoIG1lZGlhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgb3V0bGllciBjb3VudApsZWdlbmRfbGFiZWwgPC0gcGFzdGUoIk1lZGlhbjoiLCByb3VuZChtZWRpYW5fdmFsdWUsIDIpLCAKICAgICAgICAgICAgICAgICAgICAgICJcblN0YW5kYXJkIERldmlhdGlvbjoiLCByb3VuZChzdGRfZGV2LCAyKSwgCiAgICAgICAgICAgICAgICAgICAgICAiXG5PdXRsaWVyczoiLCBudW1fb3V0bGllcnMpCgojIEFkZCB0aGUgbGVnZW5kIHdpdGggbWVkaWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBvdXRsaWVyIGNvdW50CmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQgPSBsZWdlbmRfbGFiZWwsIHBjaCA9ICIiLCBjb2wgPSAiYmxhY2siLCBidHkgPSAibiIpCgpgYGAKCgpgYGB7cn0KCiMgVG90YWwgZGlzdGFuY2UgYnkgSURzCnRfZGlzdGFuY2VfZGYgPC0gZGFpbHlfYWN0aXZpdHlfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoaWQpICU+JQogIHN1bW1hcmlzZShhdmVyYWdlX3RfZGlzdGFuY2UgPSBtZWFuKHRvdGFsX2Rpc3RhbmNlICksIG1lZGlhbl90X2Rpc3RhbmNlID1tZWRpYW4odG90YWxfZGlzdGFuY2UpLCBuID0gbigpKQoKdF9kaXN0YW5jZV9kZgpgYGAKCgpgYGB7cn0KIyBDYWxjdWxhdGUgcGVyY2VudGFnZXMgZm9yIHRoZSBhdmVyYWdlIGNvbHVtbgphdF9sZWFzdF8xMF9hdmc8LSBzdW0odF9kaXN0YW5jZV9kZiRhdmVyYWdlX3RfZGlzdGFuY2U+PSAxMCkgLyBucm93KHRfZGlzdGFuY2VfZGYpICogMTAwCmJldHdlZW5fNV8xMF9hdmcgPC0gc3VtKHRfZGlzdGFuY2VfZGYkYXZlcmFnZV90X2Rpc3RhbmNlID49IDUgJiB0X2Rpc3RhbmNlX2RmJGF2ZXJhZ2VfdF9kaXN0YW5jZSA8IDEwKSAvIG5yb3codF9kaXN0YW5jZV9kZikgKiAxMDAKYmVsb3dfNV9hdmcgPC0gc3VtKHRfZGlzdGFuY2VfZGYkYXZlcmFnZV90X2Rpc3RhbmNlIDwgNSkgLyBucm93KHRfZGlzdGFuY2VfZGYpICogMTAwCgoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGZvciB0aGUgZGlzdGFuY2UgY2F0ZWdvcmllcwpwZXJjZW50YWdlX3RfZGlzdGFuY2VfZGY8LSBkYXRhLmZyYW1lKAogIENhdGVnb3J5ID0gYygiQmVsb3cgNSBrbSIsICJCZXR3ZWVuIDUgYW5kIDEwIGttIiwgIkF0IGxlYXN0IDEwIGttIiksCiAgUGVyY2VudGFnZV9BdmVyYWdlID0gcm91bmQoYyhiZWxvd181X2F2ZywgYmV0d2Vlbl81XzEwX2F2ZyAsIGF0X2xlYXN0XzEwX2F2ZykpKQpwZXJjZW50YWdlX3RfZGlzdGFuY2VfZGYKCgoKIyBDb252ZXJ0IENhdGVnb3J5IHRvIGEgZmFjdG9yIHdpdGggY3VzdG9tIGZhY3RvciBsZXZlbHMKcGVyY2VudGFnZV90X2Rpc3RhbmNlX2RmJENhdGVnb3J5IDwtIGZhY3RvcihwZXJjZW50YWdlX3RfZGlzdGFuY2VfZGYkQ2F0ZWdvcnksIGxldmVscyA9IGMoIkJlbG93IDUga20iLCAiQmV0d2VlbiA1IGFuZCAxMCBrbSIsICJBdCBsZWFzdCAxMCBrbSIpKQoKCmBgYAoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInIH0KIyBDcmVhdGUgYSBiYXIgcGxvdCB1c2luZyBnZ3Bsb3QKZ2dwbG90KHBlcmNlbnRhZ2VfdF9kaXN0YW5jZV9kZiwgYWVzKHggPSBDYXRlZ29yeSwgeSA9IFBlcmNlbnRhZ2VfQXZlcmFnZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJwaW5rIikgKwogIGxhYnMoeCA9ICJBdmVyYWdlIFRvdGFsIERpc3RhbmNlIiwgeSA9ICJQZXJjZW50YWdlIG9mIFVzZXJzIiwgdGl0bGUgPSAiNTUlIG9mIFVzZXJzIEF2ZXJhZ2UgNS0xMCBLaWxvbWV0ZXJzIERhaWx5IixzdWJ0aXRsZSA9ICIxMCwwMDAgc3RlcHMgaXMgYXBwcm94aW1hdGVseSBlcXVhbCB0byBjb3ZlcmluZyA1IG1pbGVzIChvciA4IGtpbG9tZXRlcnMpIikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAoUGVyY2VudGFnZV9BdmVyYWdlLCAiJSIpKSwgdmp1c3QgPSAtMC41LCBjb2xvciA9ICJibGFjayIpICsgCiAgeWxpbSgwLCAxMDApICsgIHRoZW1lX21pbmltYWwoKSArdGhlbWUocGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSkKCmBgYAoKCiMjIyMgU2VkZW50YXJ5IE1pbnV0ZXM6IFRvdGFsIG1pbnV0ZXMgc3BlbnQgaW4gc2VkZW50YXJ5IGFjdGl2aXR5LgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInIH0KIyBDcmVhdGUgYSBib3hwbG90IGZvciBzZWRlbnRhcnlfbWludXRlcwpib3hwbG90KGRhaWx5X2FjdGl2aXR5X2NsZWFuJHNlZGVudGFyeV9taW51dGVzLAogICAgICAgIG1haW4gPSAiQm94cGxvdCBvZiBTZWRlbnRhcnkgTWludXRlcyIsCiAgICAgICAgeWxhYiA9ICJTZWRlbnRhcnkgTWludXRlcyIpCgojIENhbGN1bGF0ZSB0aGUgbWVkaWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24KbWVkaWFuX3ZhbHVlIDwtIG1lZGlhbihkYWlseV9hY3Rpdml0eV9jbGVhbiRzZWRlbnRhcnlfbWludXRlcykKc3RkX2RldiA8LSBzZChkYWlseV9hY3Rpdml0eV9jbGVhbiRzZWRlbnRhcnlfbWludXRlcykKCiMgSWRlbnRpZnkgb3V0bGllcnMKb3V0bGllcnMgPC0gYm94cGxvdC5zdGF0cyhkYWlseV9hY3Rpdml0eV9jbGVhbiRzZWRlbnRhcnlfbWludXRlcykkb3V0CgojIENvdW50IHRoZSBudW1iZXIgb2Ygb3V0bGllcnMKbnVtX291dGxpZXJzIDwtIGxlbmd0aChvdXRsaWVycykKCiMgQ3JlYXRlIHRoZSBsZWdlbmQgbGFiZWwgd2l0aCBtZWRpYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIG91dGxpZXIgY291bnQKbGVnZW5kX2xhYmVsIDwtIHBhc3RlKCJNZWRpYW46Iiwgcm91bmQobWVkaWFuX3ZhbHVlLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICJcblN0YW5kYXJkIERldmlhdGlvbjoiLCByb3VuZChzdGRfZGV2LCAyKSwKICAgICAgICAgICAgICAgICAgICAgICJcbk91dGxpZXJzOiIsIG51bV9vdXRsaWVycykKCiMgQWRkIHRoZSBsZWdlbmQgd2l0aCBtZWRpYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIG91dGxpZXIgY291bnQKbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGxlZ2VuZF9sYWJlbCwgcGNoID0gIiIsIGNvbCA9ICJibGFjayIsIGJ0eSA9ICJuIiwgY2V4ID0gMC44MCkKCgpgYGAKCgotIFRoZXNlIGFyZSBoaWdoIHZhbHVlcyBmb3Igc2VkZW50YXJ5IG1pbnV0ZXMuIEZvciBpbnN0YW5jZSwgMTAyMCBtaW51dGVzIGlzIGVxdWl2YWxlbnQgdG8gMTcgaG91cnMsIGFuZCAxNDAwIG1pbnV0ZXMgaXMgZXF1aXZhbGVudCB0byAyNCBob3Vycy4gQWZ0ZXIgcGVyZm9ybWluZyBhIHF1aWNrIHNlYXJjaCwgaXQgc2VlbXMgdGhhdCB0aGUKW0ZpdGJpdCB1c2VzIDE0MDBdKGh0dHBzOi8vY29tbXVuaXR5LmZpdGJpdC5jb20vdDUvT3RoZXItQ2hhcmdlLVRyYWNrZXJzL3NlZGVudGFyeS1taW51dGVzL3RkLXAvMzM3MjYyMSkgYXMgZGVmYXVsdCBmb3Igc2VkZW50YXJ5IG1pbnV0ZXMgd2hlbiB0aGUgZGV2aWNlIGlzIG5vdCB3b3JuIGFuZCBpdCBpbmNsdWRlcyB0aGUgc2xlZXBpbmcgdGltZS4gClNlZGVudGFyeU1pbnV0ZXMgaXMgdG90YWwgbWludXRlcyBzcGVudCBpbiBzZWRlbnRhcnkgYWN0aXZpdHkgYWNjb3JkaW5nIHRvIHRoZSBkYXRhIGRpY3Rpb25hcnkuIFNlZSBtZXRhIGRhdGEgc2VjdGlvbi4gVGhlcmVmb3JlLCB3ZSBuZWVkIHRvIHN1YnN0cmFjdCB0aGUgdGltZXMgc2xlZXBpbmcgdG8gb2J0YWluIGFuIG1vcmUgYWNjdXJhdGUgZXN0aW1hdGUgb2YgZGFpbHkgc2VkZW50YXJ5IG1pbnV0ZXMuIAoKW1NsZWVwIHRpbWUgaXMgbm90IGNvbnNpZGVyZWQgc2VkZW50YXJ5IHRpbWUsIHNvIGl0IHdhcyByZW1vdmVkIHRvIGRldGVybWluZSB0aGUgd2FraW5nIGRheSBhbmQgdG8gYWxsb3cgdGhlIHByb3BvcnRpb24gb2YgdGhlIGRheSBzcGVudCBzZWRlbnRhcnkgdG8gYmUgY2FsY3VsYXRlZF0oaHR0cHM6Ly93d3cubWRwaS5jb20vMTY2MC00NjAxLzE4LzgvMzkxNCkKCgpgYGB7cn0KIyBDaGVjayBzZWRlbnRhcnlfbWludXRlcyBzdGF0cwpkYWlseV9hY3Rpdml0eV9jbGVhbiRzZWRlbnRhcnlfbWludXRlcyAlPiUgc3VtbWFyeSgpCgpgYGAKCmBgYHtyfQpvdXRsaWVycwpgYGAKCgpgYGB7cn0KIyBDb3VudCBlbnRyaWVzIHdoZXJlIHNlZGVudGFyeSBtaW51dGVzIGVxdWFsIDE0NDAKY291bnRfMTQ0MCA8LSBzdW0oZGFpbHlfYWN0aXZpdHlfY2xlYW4kc2VkZW50YXJ5X21pbnV0ZXMgPT0gMTQ0MCkKCiMgT3V0cHV0IHRoZSBjb3VudApjb3VudF8xNDQwCgpgYGAKYGBge3J9CiMgUmVtb3ZlIHJvd3Mgd2l0aCBzZWRlbnRhcnkgbWludXRlcyBlcXVhbCB0byB0aGUgZGVmYXVsdCB2YWx1ZSAoMTQ0MCkgYW5kIG91dGxpZXJzCgpkYWlseV9hY3Rpdml0eV9jbGVhbiA8LSBmaWx0ZXIoZGFpbHlfYWN0aXZpdHlfY2xlYW4sICEoc2VkZW50YXJ5X21pbnV0ZXMgJWluJSBjKDAsIDIsIDEzLCAxNDQwKSkpCgpgYGAKCgoKYGBge3J9CgoKIyBSZW5hbWUgdGhlIGNvbHVtbgpkYWlseV9zbGVlcF9jbGVhbiA8LSByZW5hbWUoZGFpbHlfc2xlZXBfY2xlYW4sIGFjdGl2aXR5X2RhdGUgPSBzbGVlcF9kYXkpCgojIEpvaW4gdGhlIGRhdGFzZXRzCmpvaW5lZF9hY3Rpdml0eV9zbGVlcCA8LSBpbm5lcl9qb2luKGRhaWx5X2FjdGl2aXR5X2NsZWFuLCBkYWlseV9zbGVlcF9jbGVhbiwgYnkgPSBjKCJpZCIsICJhY3Rpdml0eV9kYXRlIikpCgoKIyBDaGVjayBtaXNzaW5nIHZhbHVlcyBhbmQgZHVwbGljYXRlcwpjYXQoCiAgIlxuIiwKICAiTWlzc2luZyB2YWx1ZXM6IiwKICBzdW0oaXMubmEoam9pbmVkX2FjdGl2aXR5X3NsZWVwICkpLAogICJcbiIsCiAgIkR1cGxpY2F0ZSB2YWx1ZXM6IiwKICBzdW0oZHVwbGljYXRlZChqb2luZWRfYWN0aXZpdHlfc2xlZXAgKSksCiAgIlxuIiwKICAiVW5pcXVlIElkczoiLAogIG5fZGlzdGluY3Qoam9pbmVkX2FjdGl2aXR5X3NsZWVwICRpZCkKKQoKYGBgCgoKYGBge3J9CiMgQ3JlYXRlIGEgZGVyaXZlZCBjb2x1bW4gZm9yIHNlZGVudGFyeSBtaW51dGVzIHRoYXQgZG9lcyBub3QgaW5jbHVkZSBzbGVlcCB0aW1lCmpvaW5lZF9hY3Rpdml0eV9zbGVlcCA8LSBqb2luZWRfYWN0aXZpdHlfc2xlZXAgJT4lCiAgbXV0YXRlKAogICAgc2VkZW50YXJ5X21pbl9hd2FrZSA9IHNlZGVudGFyeV9taW51dGVzIC0gdG90YWxfbWludXRlc19hc2xlZXAsCiAgICBzZWRlbnRhcnlfaG91cnNfYXdha2UgPSBzZWRlbnRhcnlfbWluX2F3YWtlIC8gNjAsCiAgICBzZWRlbnRhcnlfcGVyY2VudGFnZV9kaWZmID0gKHNlZGVudGFyeV9taW51dGVzIC0gc2VkZW50YXJ5X21pbl9hd2FrZSkgLyBzZWRlbnRhcnlfbWludXRlcyAqIDEwMAogICkKCmBgYAoKCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicgfQoKIyBMZXQgdXMgY2hlY2sgdGhlIHBlcmNlbnRhZ2UgZGlmZmVyZW5jZSBvZiBzZWRlbnRhcnlfbWludXRlcyBhbmQgdGhlIG5ldyBjb2x1bW4gInNlZGVudGFyeV9taW5fYXdha2UKCiMgQ3JlYXRlIGEgYm94cGxvdCBmb3Igc2VkZW50YXJ5X3BlcmNlbnRhZ2VfZGlmZgpib3hwbG90KGpvaW5lZF9hY3Rpdml0eV9zbGVlcCRzZWRlbnRhcnlfcGVyY2VudGFnZV9kaWZmLAogICAgICAgIG1haW4gPSAiQm94cGxvdCBvZiBTZWRlbnRhcnkgUGVyY2VudGFnZSBEaWZmZXJlbmNlIiwKICAgICAgICB5bGFiID0gIlNlZGVudGFyeSBQZXJjZW50YWdlIERpZmZlcmVuY2UiKQoKIyBDYWxjdWxhdGUgdGhlIG1lZGlhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uCm1lZGlhbl92YWx1ZSA8LSBtZWRpYW4oam9pbmVkX2FjdGl2aXR5X3NsZWVwJHNlZGVudGFyeV9wZXJjZW50YWdlX2RpZmYpCnN0ZF9kZXYgPC0gc2Qoam9pbmVkX2FjdGl2aXR5X3NsZWVwJHNlZGVudGFyeV9wZXJjZW50YWdlX2RpZmYpCgojIElkZW50aWZ5IG91dGxpZXJzCm91dGxpZXJzIDwtIGJveHBsb3Quc3RhdHMoam9pbmVkX2FjdGl2aXR5X3NsZWVwJHNlZGVudGFyeV9wZXJjZW50YWdlX2RpZmYpJG91dAoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIG91dGxpZXJzCm51bV9vdXRsaWVycyA8LSBsZW5ndGgob3V0bGllcnMpCgojIENyZWF0ZSB0aGUgbGVnZW5kIGxhYmVsIHdpdGggbWVkaWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBvdXRsaWVyIGNvdW50CmxlZ2VuZF9sYWJlbCA8LSBwYXN0ZSgiTWVkaWFuOiIsIHJvdW5kKG1lZGlhbl92YWx1ZSwgMiksCiAgICAgICAgICAgICAgICAgICAgICAiXG5TdGFuZGFyZCBEZXZpYXRpb246Iiwgcm91bmQoc3RkX2RldiwgMiksCiAgICAgICAgICAgICAgICAgICAgICAiXG5PdXRsaWVyczoiLCBudW1fb3V0bGllcnMpCgojIEFkZCB0aGUgbGVnZW5kIHdpdGggbWVkaWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBvdXRsaWVyIGNvdW50CmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQgPSBsZWdlbmRfbGFiZWwsIHBjaCA9ICIiLCBjb2wgPSAiYmxhY2siLCBidHkgPSAibiIsIGNleCA9IDAuODApCgpgYGAKLSBUaGUgc2VkZW50YXJ5IHBlcmNlbnRhZ2UgZGlmZmVyZW5jZSBoYXMgYSBtZWRpYW4gdmFsdWUgb2YgNTkuOTUlLCBpbmRpY2F0aW5nIGEgc2lnbmlmaWNhbnQgZGlzdGluY3Rpb24gYmV0d2VlbiBzZWRlbnRhcnlfbWludXRlcyBhbmQgc2VkZW50YXJ5X21pbl9hd2FrZS4gVGhpcyBzdWdnZXN0IHRoYXQgdGhlIG9yaWdpbmFsIGNvbHVtbiAic2VkZW50YXJ5X21pbnV0ZXMiIGluY2x1ZGVkIHRoZSB0aW1lIGFzbGVlcC4KCgoKCgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInIH0KIyBDcmVhdGUgYSBib3hwbG90IGZvciBzZWRlbnRhcnlfbWluX2F3YWtlCmJveHBsb3Qoam9pbmVkX2FjdGl2aXR5X3NsZWVwJHNlZGVudGFyeV9taW5fYXdha2UsCiAgICAgICAgbWFpbiA9ICJCb3hwbG90IG9mIFNlZGVudGFyeSBNaW51dGVzIEF3YWtlIiwKICAgICAgICB5bGFiID0gIlNlZGVudGFyeSBNaW51dGVzIEF3YWtlIikKCiMgQ2FsY3VsYXRlIHRoZSBtZWRpYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbgptZWRpYW5fdmFsdWUgPC0gbWVkaWFuKGpvaW5lZF9hY3Rpdml0eV9zbGVlcCRzZWRlbnRhcnlfbWluX2F3YWtlKQpzdGRfZGV2IDwtIHNkKGpvaW5lZF9hY3Rpdml0eV9zbGVlcCRzZWRlbnRhcnlfbWluX2F3YWtlKQoKIyBJZGVudGlmeSBvdXRsaWVycwpvdXRsaWVycyA8LSBib3hwbG90LnN0YXRzKGpvaW5lZF9hY3Rpdml0eV9zbGVlcCRzZWRlbnRhcnlfbWluX2F3YWtlKSRvdXQKCiMgQ291bnQgdGhlIG51bWJlciBvZiBvdXRsaWVycwpudW1fb3V0bGllcnMgPC0gbGVuZ3RoKG91dGxpZXJzKQoKIyBDcmVhdGUgdGhlIGxlZ2VuZCBsYWJlbCB3aXRoIG1lZGlhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgb3V0bGllciBjb3VudApsZWdlbmRfbGFiZWwgPC0gcGFzdGUoIk1lZGlhbjoiLCByb3VuZChtZWRpYW5fdmFsdWUsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIlxuU3RhbmRhcmQgRGV2aWF0aW9uOiIsIHJvdW5kKHN0ZF9kZXYsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIlxuT3V0bGllcnM6IiwgbnVtX291dGxpZXJzKQoKIyBBZGQgdGhlIGxlZ2VuZCB3aXRoIG1lZGlhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgb3V0bGllciBjb3VudApsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gbGVnZW5kX2xhYmVsLCBwY2ggPSAiIiwgY29sID0gImJsYWNrIiwgYnR5ID0gIm4iLCBjZXggPSAwLjgwKQoKYGBgCi0gT2JzZXJ2YXRpb246IFRoZXJlIGFwcGVhcnMgdG8gYmUgYW4gaW5jb25zaXN0ZW5jeSBpbiB0aGUgZGF0YS4gVGhlIHNlZGVudGFyeV9taW51dGVzIHZhbHVlIGlzIHNtYWxsZXIgdGhhbiB0aGUgdG90YWxfbWludXRlc19hc2xlZXAgdmFsdWUsIHdoaWNoIGlzIHVuZXhwZWN0ZWQuIAoKCmBgYHtyfQojIENvdW50IHRoZSBudW1iZXIgb2YgY2FzZXMgd2hlcmUgc2VkZW50YXJ5X21pbnV0ZXMgaXMgc21hbGxlciB0aGFuIHRvdGFsX21pbnV0ZXNfYXNsZWVwCmNvdW50IDwtIHN1bShqb2luZWRfYWN0aXZpdHlfc2xlZXAkc2VkZW50YXJ5X21pbnV0ZXMgPCBqb2luZWRfYWN0aXZpdHlfc2xlZXAkdG90YWxfbWludXRlc19hc2xlZXApCgojIFByaW50IHRoZSBjb3VudApjb3VudAoKCmBgYAoKCmBgYHtyfQojIFN1YnNldCB0aGUgZGF0YXNldApzdWJzZXRfZGF0YSA8LSBqb2luZWRfYWN0aXZpdHlfc2xlZXBbam9pbmVkX2FjdGl2aXR5X3NsZWVwJHNlZGVudGFyeV9taW51dGVzIDwgam9pbmVkX2FjdGl2aXR5X3NsZWVwJHRvdGFsX21pbnV0ZXNfYXNsZWVwLCBdCgojIFZpZXcgdGhlIHN1YnNldHRlZCBkYXRhCnN1YnNldF9kYXRhCgpgYGAKCgoKYGBge3J9CiMgQ2hlY2sgY29sdW1uIG5hbWVzIG9mIHRoZSBzdWJzZXR0ZWQgZGF0YQpzdWJzZXRfZGF0YSAlPiUKc2VsZWN0KHNlZGVudGFyeV9taW51dGVzLCB0b3RhbF9taW51dGVzX2FzbGVlcCwgc2VkZW50YXJ5X21pbl9hd2FrZSwgY2Fsb3JpZXMsaWQsIGFjdGl2aXR5X2RhdGUsIHRvdGFsX3N0ZXBzLCB0b3RhbF9kaXN0YW5jZSwgdmVyeV9hY3RpdmVfbWludXRlcyApCmBgYAoKCmBgYHtyfQpkaW0oc3Vic2V0X2RhdGEpCmBgYApgYGB7cn0KZGltKGpvaW5lZF9hY3Rpdml0eV9zbGVlcCkKYGBgCgoKCmBgYHtyfQojIFVzZSBhbnRpX2pvaW4oKSB0byByZXR1cm4gYSBuZXcgZGF0YXNldCB0aGF0IGluY2x1ZGVzIGFsbCByb3dzIGZyb20gdGhlIGZpcnN0IGRhdGFzZXQgZXhjZXB0IGZvciB0aGUgcm93cyB0aGF0IGhhdmUgYSBtYXRjaCBpbiB0aGUgc2Vjb25kIGRhdGFzZXQuCiBjbGVhbl9zdWJzZXQ8LSBhbnRpX2pvaW4oam9pbmVkX2FjdGl2aXR5X3NsZWVwLCBzdWJzZXRfZGF0YSkKZGltKGNsZWFuX3N1YnNldCkKYGBgCgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInIH0KIyBDcmVhdGUgYSBib3hwbG90IGZvciBzZWRlbnRhcnlfbWluX2F3YWtlCmJveHBsb3QoY2xlYW5fc3Vic2V0JHNlZGVudGFyeV9taW5fYXdha2UsCiAgICAgICAgbWFpbiA9ICJCb3hwbG90IG9mIFNlZGVudGFyeSBNaW51dGVzIEF3YWtlIiwKICAgICAgICB5bGFiID0gIlNlZGVudGFyeSBNaW51dGVzIEF3YWtlIikKCiMgQ2FsY3VsYXRlIHRoZSBtZWRpYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbgptZWRpYW5fdmFsdWUgPC0gbWVkaWFuKGNsZWFuX3N1YnNldCRzZWRlbnRhcnlfbWluX2F3YWtlKQpzdGRfZGV2IDwtIHNkKGNsZWFuX3N1YnNldCRzZWRlbnRhcnlfbWluX2F3YWtlKQoKIyBJZGVudGlmeSBvdXRsaWVycwpvdXRsaWVycyA8LSBib3hwbG90LnN0YXRzKGNsZWFuX3N1YnNldCRzZWRlbnRhcnlfbWluX2F3YWtlKSRvdXQKCiMgQ291bnQgdGhlIG51bWJlciBvZiBvdXRsaWVycwpudW1fb3V0bGllcnMgPC0gbGVuZ3RoKG91dGxpZXJzKQoKIyBDcmVhdGUgdGhlIGxlZ2VuZCBsYWJlbCB3aXRoIG1lZGlhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgb3V0bGllciBjb3VudApsZWdlbmRfbGFiZWwgPC0gcGFzdGUoIk1lZGlhbjoiLCByb3VuZChtZWRpYW5fdmFsdWUsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIlxuU3RhbmRhcmQgRGV2aWF0aW9uOiIsIHJvdW5kKHN0ZF9kZXYsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIlxuT3V0bGllcnM6IiwgbnVtX291dGxpZXJzKQoKIyBBZGQgdGhlIGxlZ2VuZCB3aXRoIG1lZGlhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgb3V0bGllciBjb3VudApsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gbGVnZW5kX2xhYmVsLCBwY2ggPSAiIiwgY29sID0gImJsYWNrIiwgYnR5ID0gIm4iLCBjZXggPSAwLjgwKQoKYGBgCk9ic2VydmF0aW9uOiBCeSBlbGltaW5hdGluZyBuZWdhdGl2ZSB2YWx1ZXMgZnJvbSAic2VkZW50YXJ5X21pbl9hd2FrZSwiIHRoZSByZXN1bHRpbmcgdmFsdWVzIG5vdyByZWZsZWN0IGEgbW9yZSByZWFsaXN0aWMgc2NlbmFyaW8uCgoKYGBge3J9CiMgVG90YWwgc2VkZW50YXJ5IG1pbnV0ZXMgYXdha2UgYnkgSURzCnRfc2VkZW50YXJ5X2RmIDwtIGNsZWFuX3N1YnNldCAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKGF2ZXJhZ2Vfc2VkZW50YXJ5X21pbl9hd2FrZSA9IG1lYW4oc2VkZW50YXJ5X21pbl9hd2FrZSksCiAgICAgICAgICAgIG1lZGlhbl9zZWRlbnRhcnlfbWluX2F3YWtlID0gbWVkaWFuKHNlZGVudGFyeV9taW5fYXdha2UpLCBuID0gbigpKQoKdF9zZWRlbnRhcnlfZGYKCmBgYAoKCgpgYGB7cn0KZGF0YXNldCA8LSB0X3NlZGVudGFyeV9kZgpjb2x1bW4gPC0gImF2ZXJhZ2Vfc2VkZW50YXJ5X21pbl9hd2FrZSIKbmV3X2NhdGVnb3JpZXMgPC0gYygiQmVsb3cgMjAwIG1pbnV0ZXMiLCAiQmV0d2VlbiAyMDAgYW5kIDQwMCBtaW51dGVzIiwgIkF0IGxlYXN0IDQwMCBtaW51dGVzIikKCiMgQ2FsY3VsYXRlIHBlcmNlbnRhZ2VzIGZvciB0aGUgYXZlcmFnZSBjb2x1bW4KYmVsb3dfMjAwX2F2ZyA8LSBzdW0oZGF0YXNldFtbY29sdW1uXV0gPCAyMDApIC8gbnJvdyhkYXRhc2V0KSAqIDEwMApiZXR3ZWVuXzIwMF80MDBfYXZnIDwtIHN1bShkYXRhc2V0W1tjb2x1bW5dXSA+PSAyMDAgJiBkYXRhc2V0W1tjb2x1bW5dXSA8PSA0MDApIC8gbnJvdyhkYXRhc2V0KSAqIDEwMAphdF9sZWFzdF80MDBfYXZnIDwtIHN1bShkYXRhc2V0W1tjb2x1bW5dXSA+PSA0MDApIC8gbnJvdyhkYXRhc2V0KSAqIDEwMAoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGZvciB0aGUgY2F0ZWdvcmllcwpwZXJjZW50YWdlX3NlZGVudGFyeV9hd2FrZV9kZiA8LSBkYXRhLmZyYW1lKAogIENhdGVnb3J5ID0gbmV3X2NhdGVnb3JpZXMsCiAgUGVyY2VudGFnZV9BdmVyYWdlID0gcm91bmQoYyhiZWxvd18yMDBfYXZnLCBiZXR3ZWVuXzIwMF80MDBfYXZnLCBhdF9sZWFzdF80MDBfYXZnKSkKKQoKIyBDb252ZXJ0IENhdGVnb3J5IHRvIGEgZmFjdG9yIHdpdGggY3VzdG9tIGZhY3RvciBsZXZlbHMKcGVyY2VudGFnZV9zZWRlbnRhcnlfYXdha2VfZGYkQ2F0ZWdvcnkgPC0gZmFjdG9yKHBlcmNlbnRhZ2Vfc2VkZW50YXJ5X2F3YWtlX2RmJENhdGVnb3J5LCBsZXZlbHMgPSBuZXdfY2F0ZWdvcmllcykKCnBlcmNlbnRhZ2Vfc2VkZW50YXJ5X2F3YWtlX2RmCgoKCmBgYAoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInIH0KIyBDcmVhdGUgYSBiYXIgcGxvdCB1c2luZyBnZ3Bsb3QKZ2dwbG90KHBlcmNlbnRhZ2Vfc2VkZW50YXJ5X2F3YWtlX2RmLCBhZXMoeCA9IENhdGVnb3J5LCB5ID0gUGVyY2VudGFnZV9BdmVyYWdlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImdyYXkiKSArCiAgbGFicyh4ID0gIkF2ZXJhZ2UgVG90YWwgU2VkZW50YXJ5IE1pbiBBd2FrZSIsIHkgPSAiUGVyY2VudGFnZSBvZiBVc2VycyIsIAogICAgICAgdGl0bGUgPSAiNDglIG9mIFVzZXJzIEhhdmUgYW4gQXZlcmFnZSBvZiBhdCBMZWFzdCA0MDAgRGFpbHkgU2VkZW50YXJ5IE1pbnV0ZXMgV2hpbGUgQXdha2UiLAogICAgICAgc3VidGl0bGUgPSAiMjAwIE1pbnV0ZXMgYXJlIDMgaG91cnMgYW5kIDIwIG1pbnV0ZXM7IDQwMCBtaW4gYXJlIDYgaG91cnMgYW5kIDQwIG1pbiIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKFBlcmNlbnRhZ2VfQXZlcmFnZSwgIiUiKSksIHZqdXN0ID0gLTAuNSwgY29sb3IgPSAiYmxhY2siKSArIAogIHlsaW0oMCwgMTAwKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkKCmBgYAoKSW4gYSByZXByZXNlbnRhdGl2ZSBzYW1wbGUgb2YgVS5TLiBhZHVsdHMsIG92ZXIgdHdvLXRoaXJkcyBzcGVudCA2ICsgaG91cnMvZGF5IHNpdHRpbmcsIGFuZCBtb3JlIHRoYW4gaGFsZiBkaWQgbm90IG1lZXQgdGhlIHJlY29tbWVuZGVkIDE1MCBtaW4vd2VlayBvZiBwaHlzaWNhbCBhY3Rpdml0eS4gVGhlIHN0dWR5IGRpc2NvdmVyZWQgdGhhdCBwcm9sb25nZWQgc2l0dGluZyBmb3IgNisgaG91cnMvZGF5IHdhcyBhc3NvY2lhdGVkIHdpdGggaGlnaGVyIGJvZHkgZmF0IHBlcmNlbnRhZ2VzLiBXaGlsZSBleGNlZWRpbmcgMTUwIG1pbi93ZWVrIG9mIHBoeXNpY2FsIGFjdGl2aXR5IHdhcyBsaW5rZWQgdG8gbG93ZXIgYm9keSBmYXQgcGVyY2VudGFnZXMsIGFjaGlldmluZyByZWNvbW1lbmRlZCBhY3Rpdml0eSBsZXZlbHMgbWF5IG5vdCBmdWxseSBvZmZzZXQgdGhlIGluY3JlYXNlZCBib2R5IGZhdCBmcm9tIHByb2xvbmdlZCBzaXR0aW5nLiAKCkppbmd3ZW4gTGlhbywgTWluIEh1LCBLZWxsaWUgSW1tLCBDbGlmdG9uIEouIEhvbG1lcywgSmllIFpodSwgQ2hhbyBDYW8sIExpbiBZYW5nLiBBc3NvY2lhdGlvbiBvZiBkYWlseSBzaXR0aW5nIHRpbWUgYW5kIGxlaXN1cmUtdGltZSBwaHlzaWNhbCBhY3Rpdml0eSB3aXRoIGJvZHkgZmF0IGFtb25nIFUuUy4gYWR1bHRzLiBKb3VybmFsIG9mIFNwb3J0IGFuZCBIZWFsdGggU2NpZW5jZSwgMjAyMi4gSVNTTiAyMDk1LTI1NDYuIGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE2L2ouanNocy4yMDIyLjEwLjAwMS4gKGh0dHBzOi8vd3d3LnNjaWVuY2VkaXJlY3QuY29tL3NjaWVuY2UvYXJ0aWNsZS9waWkvUzIwOTUyNTQ2MjIwMDEwMTYpCgoKCiMjIyMgQ2Fsb3JpZXM6VG90YWwgZXN0aW1hdGVkIGVuZXJneSBleHBlbmRpdHVyZSAoaW4ga2lsb2NhbG9yaWVzKS4gCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJyB9CiMgQ3JlYXRlIGEgYm94cGxvdCBmb3IgY2Fsb3JpZXMKYm94cGxvdChkYWlseV9hY3Rpdml0eV9jbGVhbiRjYWxvcmllcywgCiAgICAgICAgbWFpbiA9ICJCb3hwbG90IG9mIENhbG9yaWVzIiwKICAgICAgICB5bGFiID0gIkNhbG9yaWVzIikKCiMgQ2FsY3VsYXRlIHRoZSBtZWRpYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbgptZWRpYW5fdmFsdWUgPC0gbWVkaWFuKGRhaWx5X2FjdGl2aXR5X2NsZWFuJGNhbG9yaWVzKQpzdGRfZGV2IDwtIHJvdW5kKHNkKGRhaWx5X2FjdGl2aXR5X2NsZWFuJGNhbG9yaWVzKSwyKQoKIyBJZGVudGlmeSBvdXRsaWVycwpvdXRsaWVycyA8LSBib3hwbG90LnN0YXRzKGRhaWx5X2FjdGl2aXR5X2NsZWFuJGNhbG9yaWVzKSRvdXQKCiMgQ291bnQgdGhlIG51bWJlciBvZiBvdXRsaWVycwpudW1fb3V0bGllcnMgPC0gbGVuZ3RoKG91dGxpZXJzKQoKIyBDcmVhdGUgdGhlIGxlZ2VuZCBsYWJlbCB3aXRoIG1lZGlhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgb3V0bGllciBjb3VudApsZWdlbmRfbGFiZWwgPC0gcGFzdGUoIk1lZGlhbjoiLCBtZWRpYW5fdmFsdWUsIAogICAgICAgICAgICAgICAgICAgICAgIlxuU3RhbmRhcmQgRGV2aWF0aW9uOiIsIHN0ZF9kZXYsIAogICAgICAgICAgICAgICAgICAgICAgIlxuT3V0bGllcnM6IiwgbnVtX291dGxpZXJzKQoKIyBBZGQgdGhlIGxlZ2VuZCB3aXRoIG1lZGlhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgb3V0bGllciBjb3VudApsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gbGVnZW5kX2xhYmVsLCBwY2ggPSAiIiwgY29sID0gImJsYWNrIiwgYnR5ID0gIm4iLCBjZXggPSAwLjg1KQoKYGBgCgoKYGBge3J9Cm91dGxpZXJzCmBgYAoKYGBge3J9CiMgQ2Fsb3JpZXMgYXZlcmFnZXMgYnkgSURzCmNhbG9yaWVzX2RmIDwtIGRhaWx5X2FjdGl2aXR5X2NsZWFuICU+JQogIGdyb3VwX2J5KGlkKSAlPiUKICBzdW1tYXJpc2UoYXZlcmFnZV9jYWxvcmllcyA9IG1lYW4oY2Fsb3JpZXMpLCBtZWRpYW5fY2Fsb3JpZXMgPSBtZWRpYW4oY2Fsb3JpZXMpKQoKY2Fsb3JpZXNfZGYKCmBgYAoKCgoKYGBge3J9CiMgQ2FsY3VsYXRlIHBlcmNlbnRhZ2VzIGZvciB0aGUgYXZlcmFnZSBjb2x1bW4KYmVsb3dfMTYwMF9hdmcgPC0gc3VtKGNhbG9yaWVzX2RmJGF2ZXJhZ2VfY2Fsb3JpZXMgPCAxNjAwKSAvIG5yb3coY2Fsb3JpZXNfZGYpICogMTAwCmJldHdlZW5fMTYwMF8yMjAwX2F2ZyA8LSBzdW0oY2Fsb3JpZXNfZGYkYXZlcmFnZV9jYWxvcmllcyA+PSAxNjAwICYgY2Fsb3JpZXNfZGYkYXZlcmFnZV9jYWxvcmllcyA8IDIyMDApIC8gbnJvdyhjYWxvcmllc19kZikgKiAxMDAKYmV0d2Vlbl8yMjAwXzMwMDBfYXZnIDwtIHN1bShjYWxvcmllc19kZiRhdmVyYWdlX2NhbG9yaWVzID49IDIyMDAgJiBjYWxvcmllc19kZiRhdmVyYWdlX2NhbG9yaWVzIDwgMzAwMCkgLyBucm93KGNhbG9yaWVzX2RmKSAqIDEwMAphdF9sZWFzdF8zMDAwX2F2ZyA8LSBzdW0oY2Fsb3JpZXNfZGYkYXZlcmFnZV9jYWxvcmllcyA+PSAzMDAwKSAvIG5yb3coY2Fsb3JpZXNfZGYpICogMTAwCgojIENhbGN1bGF0ZSBwZXJjZW50YWdlcyBmb3IgdGhlIG1lZGlhbiBjb2x1bW4KYmVsb3dfMTYwMF9tZWQgPC0gc3VtKGNhbG9yaWVzX2RmJG1lZGlhbl9jYWxvcmllcyA8IDE2MDApIC8gbnJvdyhjYWxvcmllc19kZikgKiAxMDAKYmV0d2Vlbl8xNjAwXzIyMDBfbWVkIDwtIHN1bShjYWxvcmllc19kZiRtZWRpYW5fY2Fsb3JpZXMgPj0gMTYwMCAmIGNhbG9yaWVzX2RmJG1lZGlhbl9jYWxvcmllcyA8IDIyMDApIC8gbnJvdyhjYWxvcmllc19kZikgKiAxMDAKYmV0d2Vlbl8yMjAwXzMwMDBfbWVkIDwtIHN1bShjYWxvcmllc19kZiRtZWRpYW5fY2Fsb3JpZXMgPj0gMjIwMCAmIGNhbG9yaWVzX2RmJG1lZGlhbl9jYWxvcmllcyA8IDMwMDApIC8gbnJvdyhjYWxvcmllc19kZikgKiAxMDAKYXRfbGVhc3RfMzAwMF9tZWQgPC0gc3VtKGNhbG9yaWVzX2RmJG1lZGlhbl9jYWxvcmllcyA+PSAzMDAwKSAvIG5yb3coY2Fsb3JpZXNfZGYpICogMTAwCgojIENyZWF0ZSBhIGRhdGEgZnJhbWUgZm9yIHRoZSBjYWxvcmllcyBjYXRlZ29yaWVzCnBlcmNlbnRhZ2VfY2Fsb3JpZXNfZGYgPC0gZGF0YS5mcmFtZSgKICBDYXRlZ29yeSA9IGMoIkJlbG93IDEsNjAwIiwgIkJldHdlZW4gMSw2MDAgYW5kIDIsMjAwIiwgIkJldHdlZW4gMiwyMDAgYW5kIDMsMDAwIiwgIkF0IGxlYXN0IDMsMDAwIiksCiAgUGVyY2VudGFnZV9BdmVyYWdlID0gcm91bmQoYyhiZWxvd18xNjAwX2F2ZywgYmV0d2Vlbl8xNjAwXzIyMDBfYXZnLCBiZXR3ZWVuXzIyMDBfMzAwMF9hdmcsIGF0X2xlYXN0XzMwMDBfYXZnKSksCiAgUGVyY2VudGFnZV9NZWRpYW4gPSByb3VuZChjKGJlbG93XzE2MDBfbWVkLCBiZXR3ZWVuXzE2MDBfMjIwMF9tZWQsIGJldHdlZW5fMjIwMF8zMDAwX21lZCwgYXRfbGVhc3RfMzAwMF9tZWQpKQopCgojIENvbnZlcnQgQ2F0ZWdvcnkgdG8gYSBmYWN0b3Igd2l0aCBjdXN0b20gZmFjdG9yIGxldmVscwpwZXJjZW50YWdlX2NhbG9yaWVzX2RmJENhdGVnb3J5IDwtIGZhY3RvcihwZXJjZW50YWdlX2NhbG9yaWVzX2RmJENhdGVnb3J5LCBsZXZlbHMgPSBjKCJCZWxvdyAxLDYwMCIsICJCZXR3ZWVuIDEsNjAwIGFuZCAyLDIwMCIsICJCZXR3ZWVuIDIsMjAwIGFuZCAzLDAwMCIsICJBdCBsZWFzdCAzLDAwMCIpKQoKcGVyY2VudGFnZV9jYWxvcmllc19kZgoKYGBgCgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInIH0KIyBDcmVhdGUgYSBiYXIgcGxvdCB1c2luZyBnZ3Bsb3QKZ2dwbG90KHBlcmNlbnRhZ2VfY2Fsb3JpZXNfZGYsIGFlcyh4ID0gQ2F0ZWdvcnksIHkgPSBQZXJjZW50YWdlX0F2ZXJhZ2UpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAicmVkIikgKwogIGxhYnMoeCA9ICJDYWxvcmllIENhdGVnb3JpZXMiLCB5ID0gIlBlcmNlbnRhZ2Ugb2YgVXNlcnMiLCAKICAgICAgIHRpdGxlID0gIjQyJSBvZiBVc2VycyBIYXZlIGFuIEF2ZXJhZ2UgRGFpbHkgQ2Fsb3JpZSBFeHBlbmRpdHVyZSBCZXR3ZWVuIDEsNjAwIGFuZCAyLDIwMC4iLAogICAgICAgc3VidGl0bGUgPSAiTW9zdCBmZW1hbGVzIHJlcXVpcmUgMSw2MDAgdG8gMiwyMDAgY2Fsb3JpZXMgcGVyIGRheSwgYXMgcGVyIHRoZSBEaWV0YXJ5IEd1aWRlbGluZXMgZm9yIEFtZXJpY2FucywgMjAyMC0yMDI1IikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAoUGVyY2VudGFnZV9BdmVyYWdlLCAiJSIpKSwgdmp1c3QgPSAtMC41LCBjb2xvciA9ICJibGFjayIpICsgCiAgeWxpbSgwLCAxMDApICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKQoKYGBgCgoKIkZlbWFsZXMgYWdlcyAxOSB0aHJvdWdoIDMwIHJlcXVpcmUgYWJvdXQgMSw4MDAgdG8gMiw0MDAgY2Fsb3JpZXMgYSBkYXkuIE1hbGVzIGluIHRoaXMgYWdlIGdyb3VwIGhhdmUgaGlnaGVyIGNhbG9yaWUgbmVlZHMgb2YgYWJvdXQgMiw0MDAgdG8gMywwMDAgYSBkYXkuIENhbG9yaWUgbmVlZHMgZm9yIGFkdWx0cyBhZ2VzIDMxIHRocm91Z2ggNTkgYXJlIGdlbmVyYWxseSBsb3dlcjsgbW9zdCBmZW1hbGVzIHJlcXVpcmUgYWJvdXQgMSw2MDAgdG8gMiwyMDAgY2Fsb3JpZXMgYSBkYXkgYW5kIG1hbGVzIHJlcXVpcmUgYWJvdXQgMiwyMDAgdG8gMywwMDAgY2Fsb3JpZXMgYSBkYXkuIiAKClUuUy4gRGVwYXJ0bWVudCBvZiBBZ3JpY3VsdHVyZSBhbmQgVS5TLiBEZXBhcnRtZW50IG9mIEhlYWx0aCBhbmQgSHVtYW4gU2VydmljZXMuIERpZXRhcnkgR3VpZGVsaW5lcyBmb3IgQW1lcmljYW5zLCAyMDIwLTIwMjUuIDl0aCBFZGl0aW9uLiBEZWNlbWJlciAyMDIwLiBBdmFpbGFibGUgYXQgRGlldGFyeUd1aWRlbGluZXMuZ292LyAgCgoKCiMjIyMgSW50ZW5zaXR5IE1pbnV0ZXM6IFRpbWUgc3BlbnQgaW4gb25lIG9mIGZvdXIgaW50ZW5zaXR5IGNhdGVnb3JpZXMuCgotIFZlcnlBY3RpdmVNaW51dGVzOiBUb3RhbCBtaW51dGVzIHNwZW50IGluIHZlcnkgYWN0aXZlIGFjdGl2aXR5CgotIEZhaXJseUFjdGl2ZU1pbnV0ZXM6IFRvdGFsIG1pbnV0ZXMgc3BlbnQgaW4gbW9kZXJhdGUgYWN0aXZpdHkKCi0gTGlnaHRseUFjdGl2ZU1pbnV0ZXM6IFRvdGFsIG1pbnV0ZXMgc3BlbnQgaW4gbGlnaHQgYWN0aXZpdHkKCi0gU2VkZW50YXJ5TWludXRlczogVG90YWwgbWludXRlcyBzcGVudCBpbiBzZWRlbnRhcnkgYWN0aXZpdHkKCgoKYGBge3J9CmFjdGl2aXR5X21pbnV0ZXNfZGYgPC0gZGFpbHlfYWN0aXZpdHlfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoaWQpICU+JQogIHN1bW1hcmlzZSgKICAgIGF2ZXJhZ2VfdmVyeV9hY3RpdmVfbWludXRlcyA9IG1lYW4odmVyeV9hY3RpdmVfbWludXRlcyksCiAgICBhdmVyYWdlX2ZhaXJseV9hY3RpdmVfbWludXRlcyA9IG1lYW4oZmFpcmx5X2FjdGl2ZV9taW51dGVzKSwKICAgIGF2ZXJhZ2VfbGlnaHRseV9hY3RpdmVfbWludXRlcyA9IG1lYW4obGlnaHRseV9hY3RpdmVfbWludXRlcyksCiAgICBhdmVyYWdlX3NlZGVudGFyeV9taW51dGVzID0gbWVhbihzZWRlbnRhcnlfbWludXRlcykKICApCgphY3Rpdml0eV9taW51dGVzX2RmCgoKYGBgCgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD04fQoKIyBEZWZpbmUgdGhlIGN1c3RvbSBvcmRlciBvZiBsZWdlbmQgaXRlbXMKY3VzdG9tX29yZGVyIDwtIGMoICJWZXJ5IEFjdGl2ZSIsICJGYWlybHkgQWN0aXZlIiwgIkxpZ2h0bHkgQWN0aXZlIiwgIlNlZGVudGFyeSIpCgojIENyZWF0ZSB0aGUgc3RhY2tlZCBiYXIgcGxvdApnZ3Bsb3QoYWN0aXZpdHlfbWludXRlc19kZiwgYWVzKHkgPSBpZCkpICsKICBnZW9tX2JhcihhZXMoeCA9IGF2ZXJhZ2Vfc2VkZW50YXJ5X21pbnV0ZXMsIGZpbGwgPSAiU2VkZW50YXJ5IiksIHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuNSkgKwogIGdlb21fYmFyKGFlcyh4ID0gYXZlcmFnZV9saWdodGx5X2FjdGl2ZV9taW51dGVzLCBmaWxsID0gIkxpZ2h0bHkgQWN0aXZlIiksIHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuNSkgKwogIGdlb21fYmFyKGFlcyh4ID0gYXZlcmFnZV9mYWlybHlfYWN0aXZlX21pbnV0ZXMsIGZpbGwgPSAiRmFpcmx5IEFjdGl2ZSIpLCBzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjUpICsKICBnZW9tX2JhcihhZXMoeCA9IGF2ZXJhZ2VfdmVyeV9hY3RpdmVfbWludXRlcywgZmlsbCA9ICJWZXJ5IEFjdGl2ZSIpLCBzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjUpICsKICB4bGFiKCJNaW51dGVzIikgKwogIHlsYWIoIklEIikgKwogIGdndGl0bGUoIkF2ZXJhZ2UgQWN0aXZpdHkgTWludXRlcyBieSBJRCIpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIiIsIHZhbHVlcyA9IGMoIlZlcnkgQWN0aXZlIiA9ICJyZWQiLCAiRmFpcmx5IEFjdGl2ZSIgPSAib3JhbmdlIiwgIkxpZ2h0bHkgQWN0aXZlIiA9ICJsaWdodGdyZWVuIiwgIlNlZGVudGFyeSIgPSAibGlnaHRibHVlIiksIGJyZWFrcyA9IGN1c3RvbV9vcmRlcikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpIAoKICAKCgoKCgoKYGBgCgoKCmBgYHtyfQojIENhbGN1bGF0ZSB0aGUgYXZlcmFnZSBmb3IgZWFjaCBjb2x1bW4KYXZlcmFnZXMgPC0gY29sTWVhbnMoYWN0aXZpdHlfbWludXRlc19kZlssIGMoImF2ZXJhZ2VfdmVyeV9hY3RpdmVfbWludXRlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhdmVyYWdlX2ZhaXJseV9hY3RpdmVfbWludXRlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhdmVyYWdlX2xpZ2h0bHlfYWN0aXZlX21pbnV0ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXZlcmFnZV9zZWRlbnRhcnlfbWludXRlcyIpXSkKCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCBhdmVyYWdlCnRvdGFsX2F2ZXJhZ2UgPC0gc3VtKGF2ZXJhZ2VzKQoKIyBDYWxjdWxhdGUgdGhlIHByb3BvcnRpb25zCnByb3BvcnRpb25zIDwtIGF2ZXJhZ2VzIC8gdG90YWxfYXZlcmFnZQoKIyBDcmVhdGUgdGhlIG5ldyBkYXRhZnJhbWUgd2l0aCBtb2RpZmllZCByb3cgbmFtZXMKb3ZlcmFsbF9hdmVyYWdlX2RmPC0gZGF0YS5mcmFtZShBdmVyYWdlID0gYXZlcmFnZXMsCiAgICAgICAgICAgICAgICAgICAgIFBlcmNlbnRhZ2UgPSBwcm9wb3J0aW9ucyAqIDEwMCkKCiMgTW9kaWZ5IHRoZSByb3cgbmFtZXMKcm93X25hbWVzIDwtIGMoIlZlcnkgQWN0aXZlIiwgIkZhaXJseSBBY3RpdmUiLCAiTGlnaHRseSBBY3RpdmUiLCAiU2VkZW50YXJ5IikKcm93Lm5hbWVzKG92ZXJhbGxfYXZlcmFnZV9kZikgPC0gcm93X25hbWVzCgojIFByaW50IHRoZSBuZXcgZGF0YWZyYW1lCm92ZXJhbGxfYXZlcmFnZV9kZgoKCmBgYAoKCgoKCgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCAgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CgpnZ3Bsb3Qob3ZlcmFsbF9hdmVyYWdlX2RmLCBhZXMoeCA9IFBlcmNlbnRhZ2UsIHkgPSByZW9yZGVyKHJvdy5uYW1lcyhvdmVyYWxsX2F2ZXJhZ2VfZGYpLCBQZXJjZW50YWdlKSwgZmlsbCA9IHJvdy5uYW1lcyhvdmVyYWxsX2F2ZXJhZ2VfZGYpKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuNywgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAocm91bmQoUGVyY2VudGFnZSksICIlIikpLCBoanVzdCA9IC0wLjIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDQpICsKICB5bGFiKCJNaW51dGVzIEludGVuc2l0eSIpICsKICB4bGFiKCJQZXJjZW50YWdlIikgKwogIGdndGl0bGUoIlVzZXJzJyBPdmVyYWxsIEF2ZXJhZ2UgSW50ZW5zaXR5IE1pbnV0ZXMgQ29uc2lzdCBQcmltYXJpbHkgb2YgU2VkZW50YXJ5IGFuZCBMaWdodGx5IEFjdGl2ZSBUaW1lIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlZlcnkgQWN0aXZlIiA9ICJyZWQiLCAiRmFpcmx5IEFjdGl2ZSIgPSAib3JhbmdlIiwgIkxpZ2h0bHkgQWN0aXZlIiA9ICJsaWdodGdyZWVuIiwgIlNlZGVudGFyeSIgPSAibGlnaHRibHVlIikpICsKIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBOVUxMKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpCgoKYGBgCgoKIkFuYWx5emluZyBlYWNoIGluZGl2aWR1YWwncyBhdmVyYWdlIGNhbG9yaWUgaW50YWtlIGNhbiBwcm92aWRlIGluc2lnaHRzIGludG8gdGhlaXIgaW5kaXZpZHVhbCBkaWV0YXJ5IGhhYml0cyBhbmQgcGF0dGVybnMuIEJ5IGNvbXBhcmluZyB0aGUgaW5kaXZpZHVhbCBhdmVyYWdlcyB0byB0aGUgb3ZlcmFsbCBhdmVyYWdlLCB5b3UgY2FuIGlkZW50aWZ5IGluZGl2aWR1YWxzIHdobyBjb25zdW1lIG1vcmUgb3IgZmV3ZXIgY2Fsb3JpZXMgY29tcGFyZWQgdG8gdGhlIGdyb3VwIGF2ZXJhZ2UuIFRoaXMgY29tcGFyaXNvbiBjYW4gaGVscCBpbiB1bmRlcnN0YW5kaW5nIHZhcmlhdGlvbnMgaW4gY2Fsb3JpZSBpbnRha2UgYW5kIHBvdGVudGlhbCBmYWN0b3JzIGluZmx1ZW5jaW5nIGluZGl2aWR1YWwgZGlmZmVyZW5jZXMuIgoKCgoKCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTN9CiMgRGVmaW5lIHRoZSBjdXN0b20gb3JkZXIgb2YgbGVnZW5kIGl0ZW1zCgpjdXN0b21fb3JkZXIgPC0gYygiVmVyeSBBY3RpdmUiLCAiRmFpcmx5IEFjdGl2ZSIsICJMaWdodGx5IEFjdGl2ZSIsICJTZWRlbnRhcnkiKQoKCiMgQ3JlYXRlIHRoZSBzdGFja2VkIGhvcml6b250YWwgYmFyIGNoYXJ0CmdncGxvdChvdmVyYWxsX2F2ZXJhZ2VfZGYsIGFlcyh4ID0gUGVyY2VudGFnZSwgeSA9IGZhY3RvcigxKSwgZmlsbCA9IGZhY3Rvcihyb3cubmFtZXMob3ZlcmFsbF9hdmVyYWdlX2RmKSwgbGV2ZWxzID0gY3VzdG9tX29yZGVyKSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjcpICsKICB4bGFiKCJQZXJjZW50YWdlIikgKwogIHlsYWIoIiIpICsKICBnZ3RpdGxlKCJVc2VycycgT3ZlcmFsbCBBdmVyYWdlIEludGVuc2l0eSBNaW51dGVzIENvbnNpc3QgUHJpbWFyaWx5IG9mIFNlZGVudGFyeSBhbmQgTGlnaHRseSBBY3RpdmUgVGltZSIpICsKICBzY2FsZV9maWxsX21hbnVhbCgKICAgIG5hbWUgPSAiIiwKICAgIHZhbHVlcyA9IGMoCiAgICAgICJWZXJ5IEFjdGl2ZSIgPSAicmVkIiwKICAgICAgIkZhaXJseSBBY3RpdmUiID0gIm9yYW5nZSIsCiAgICAgICJMaWdodGx5IEFjdGl2ZSIgPSAibGlnaHRncmVlbiIsCiAgICAgICJTZWRlbnRhcnkiID0gImxpZ2h0Ymx1ZSIKICAgICksCiAgICBicmVha3MgPSBjdXN0b21fb3JkZXIKICApICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChyZXZlcnNlID0gVFJVRSkpICsgICMgUmV2ZXJzZSB0aGUgb3JkZXIgb2YgdGhlIGxlZ2VuZAogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsIAogICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLCAgIyBSZW1vdmUgdGhlIHktYXhpcyB0ZXh0CiAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBtYXJnaW4gPSBtYXJnaW4oYiA9IDIwKSkpICsgIyBBZGp1c3QgdGhlIHRpdGxlIHNpemUgYW5kIG1hcmdpbgogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDk3LCBjb2xvciA9ICJibGFjayIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSA5NywgeSA9IDEsIGxhYmVsID0gIiAgIDk3JSIsIHZqdXN0ID0gLTUuNSwgaGp1c3QgPSAwLjEpCgoKYGBgCgoKVGhlc2UgaW5kaWNhdG9ycyBwcm92aWRlIGluc2lnaHRzIGludG8gYWN0aXZpdHkgbGV2ZWxzLCBzZWRlbnRhcnkgYmVoYXZpb3IsIGFuZCBjYWxvcmllIGJ1cm4uIFRoZXkgY2FuIGhlbHAgdHJhY2sgcHJvZ3Jlc3MsIHNldCBnb2FscywgYW5kIGV2YWx1YXRlIHVzZXIgYmVoYXZpb3Igb3ZlciB0aW1lLiBSZW1lbWJlciB0byBjb25zaWRlciB0aGUgc3BlY2lmaWMgY29udGV4dCBhbmQgZ29hbHMgb2YgeW91ciBhbmFseXNpcyB0byBzZWxlY3QgYW5kIGN1c3RvbWl6ZSB0aGUgbW9zdCByZWxldmFudCBLUElzIGZvciB5b3VyIHVzZSBjYXNlLiBUaGUgY29udGV4dCBJIHdpbGwgdXNlIGlzIHRoZSBndWlkZWxpbmVzIGZvciBwaHlzaWNhbCBhY3Rpdml0eSBhbmQgZGlldCBmb3IgQW1lcmljYW5zOgoKLSBVLlMuIERlcGFydG1lbnQgb2YgSGVhbHRoIGFuZCBIdW1hbiBTZXJ2aWNlcy4gKDIwMTkpLiBQaHlzaWNhbCBBY3Rpdml0eSBHdWlkZWxpbmVzIGZvciBBbWVyaWNhbnMgKDJuZCBlZC4pLiBBdmFpbGFibGUgYXQgaHR0cHM6Ly9oZWFsdGguZ292L3NpdGVzL2RlZmF1bHQvZmlsZXMvMjAxOS0wOS9QaHlzaWNhbF9BY3Rpdml0eV9HdWlkZWxpbmVzXzJuZF9lZGl0aW9uLnBkZiAgCi0gVS5TLiBEZXBhcnRtZW50IG9mIEFncmljdWx0dXJlIGFuZCBVLlMuIERlcGFydG1lbnQgb2YgSGVhbHRoIGFuZCBIdW1hbiBTZXJ2aWNlcy4gRGlldGFyeSBHdWlkZWxpbmVzIGZvciBBbWVyaWNhbnMsIDIwMjAtMjAyNS4gOXRoIEVkaXRpb24uIERlY2VtYmVyIDIwMjAuIEF2YWlsYWJsZSBhdCBEaWV0YXJ5R3VpZGVsaW5lcy5nb3YvICAKCgojIyAgRURBIGZvciBkYWlseV9zbGVlcF9jbGVhbgoKYGBge3J9CnN0cihkYWlseV9zbGVlcF9jbGVhbikKYGBgCi0gYWN0aXZpdHlfZGF0ZSAoc2xlZXBfZGF5KTogRGF0ZSBvbiB3aGljaCB0aGUgc2xlZXAgZXZlbnQgc3RhcnRlZC4KLSB0b3RhbF9zbGVlcF9yZWNvcmRzOiBOdW1iZXIgb2YgcmVjb3JkZWQgc2xlZXAgcGVyaW9kcyBmb3IgdGhhdCBkYXkuIEluY2x1ZGVzIG5hcHMgPiA2MCBtaW4uCi0gdG90YWxfbWludXRlc19hc2xlZXA6IFRvdGFsIG51bWJlciBvZiBtaW51dGVzIGNsYXNzaWZpZWQgYXMgYmVpbmcg4oCcYXNsZWVw4oCdLgotIHRvdGFsX3RpbWVfaW5fYmVkOiBUb3RhbCBtaW51dGVzIHNwZW50IGluIGJlZCwgaW5jbHVkaW5nIGFzbGVlcCwgcmVzdGxlc3MsIGFuZCBhd2FrZSwgdGhhdCBvY2N1cnJlZCBkdXJpbmcgYSBkZWZpbmVkIHNsZWVwIHJlY29yZC4KCgoKYGBge3J9CiNTYW5pdHkgY2hlY2s6IFZlcmlmeSB0aGF0IHRoZSB2YWx1ZSBvZiB0b3RhbF90aW1lX2luX2JlZCBpcyBncmVhdGVyIHRoYW4gdG90YWxfbWludXRlc19hc2xlZXAsIGFzIHdlIHdvdWxkIGV4cGVjdC4KZGFpbHlfc2xlZXBfY2xlYW5bZGFpbHlfc2xlZXBfY2xlYW4kdG90YWxfdGltZV9pbl9iZWQgPCBkYWlseV9zbGVlcF9jbGVhbiR0b3RhbF9taW51dGVzX2FzbGVlcCxdCmBgYAojIyMgVW5pdmFyaWF0ZSBhbmFseXNpcwoKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30KCm51bWVyaWNhbF9jb2xzIDwtIGRhaWx5X3NsZWVwX2NsZWFuJT4lCiAgc2VsZWN0X2lmKGlzLm51bWVyaWMpCgojIHBsb3R0aW5nIGFsbCBudW1lcmljYWwgdmFyaWFibGVzCmNvbF9uYW1lcyA8LSBjb2xuYW1lcyhudW1lcmljYWxfY29scyApCmZvciAoaSBpbiBjb2xfbmFtZXMpIHsKICBzdXBwcmVzc1dhcm5pbmdzKHByaW50KAogICAgZ2dwbG90KG51bWVyaWNhbF9jb2xzICwgYWVzKG51bWVyaWNhbF9jb2xzIFtbaV1dKSkgKwogICAgICBnZW9tX2hpc3RvZ3JhbSgKICAgICAgICBiaW5zID0gMzAsCiAgICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICAgIGZpbGwgPSAiZ3JheSIsCiAgICAgICAgYWVzKHkgPSAuLmRlbnNpdHkuLikKICAgICAgKSArCiAgICAgIGdlb21fZGVuc2l0eSgKICAgICAgICBjb2xvciA9ICJibHVlIiwKICAgICAgICBzaXplID0gMQogICAgICApICsKICAgICAgeGxhYihpKSArIHlsYWIoIkNvdW50IikgKwogICAgICBnZ3RpdGxlKHBhc3RlKCJIaXN0b2dyYW0gd2l0aCBEZW5zaXR5IFBsb3Qgb2YiLCBpKSkKICApKQp9CmBgYAoKCiMjIyBCaXZhcmlhdGUgYW5hbHlzaXMKCgojIyMjIENvcnJlbGF0aW9uIGJldHdlZW4gbnVtZXJpY2FsIHZhcmlhYmxlcwoKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJyB9CiMgQ29ycmVsYXRpb24gYmV0d2VlbiBudW1lcmljYWwgdmFyaWFibGVzCmNvcnIgPC0gY29yKHNlbGVjdF9pZihkYWlseV9zbGVlcF9jbGVhbiwgaXMubnVtZXJpYykpCgpnZ2NvcnJwbG90KGNvcnIsCiAgICAgICAgICAgaGMub3JkZXIgPSBUUlVFLAogICAgICAgICAgIHR5cGUgPSAibG93ZXIiLAogICAgICAgICAgIGxhYiA9IFRSVUUsCiAgICAgICAgICAgY29sb3JzID0gYygiZmlyZWJyaWNrIiwgIndoaXRlIiwgInJveWFsYmx1ZSIpLAogICAgICAgICAgIGxhYl9zaXplID0gNCwKICAgICAgICAgICBsYWJfY29sID0gImJsYWNrIiwKICAgICAgICAgICB0aXRsZSA9ICJDb3JyZWxhdGlvbiBCZXR3ZWVuIE51bWVyaWNhbCBWYXJpYWJsZXMiKQoKCmBgYAoKIyMjIyBTY2F0dGVycGxvdHMgb2YgdG90YWxfbWludXRlc19hc2xlZXAgdnMgdG90YWxfdGltZV9pbl9iZWQKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJyB9CgpnZ3Bsb3QoZGF0YSA9IGRhaWx5X3NsZWVwX2NsZWFuLCBhZXMoeCA9IHRvdGFsX21pbnV0ZXNfYXNsZWVwLCB5ID0gdG90YWxfdGltZV9pbl9iZWQpKSArCiAgZ2VvbV9wb2ludCgpCgpgYGAKCiMjIyBVc2VyIEJlaGF2aW9yIGZvciBkYWlseSBzbGVlcCBkYXRhc2V0CgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInIH0KCgpmcmVxdWVuY3lfdGFibGUgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShkYWlseV9zbGVlcF9jbGVhbiR0b3RhbF9zbGVlcF9yZWNvcmRzKSkKZnJlcXVlbmN5X3RhYmxlJFBlcmNlbnRhZ2UgPC0gZnJlcXVlbmN5X3RhYmxlJEZyZXEgLyBzdW0oZnJlcXVlbmN5X3RhYmxlJEZyZXEpICogMTAwCgpnZ3Bsb3QoZGF0YSA9IGZyZXF1ZW5jeV90YWJsZSwgYWVzKHggPSBWYXIxLCB5ID0gRnJlcSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJzdGVlbGJsdWUiKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlKEZyZXEsICIgKCIsIHBlcmNlbnQoUGVyY2VudGFnZSAvIDEwMCksICIpIiwgc2VwID0gIiIpKSwKICAgICAgICAgICAgaGp1c3QgPSAwLjUsICB2anVzdCA9IC0wLjQsIGNvbG9yID0gImJsYWNrIikgKwogIGxhYnMoeCA9ICJUb3RhbCBTbGVlcCBSZWNvcmRzIiwgeSA9ICJGcmVxdWVuY3kiLAogICAgICAgdGl0bGUgPSAiVW5jb21tb24gTmFwcGluZzogODklIG9mIFNsZWVwIFJlY29yZHMgSW5kaWNhdGUgYSBTaW5ndWxhciBTbGVlcCBQZXJpb2QuIiwKICAgICAgIHN1YnRpdGxlID0gIkluY2x1ZGVzIG5hcHMgPiA2MCBtaW4uIikrCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgbWFyZ2luID0gbWFyZ2luKGIgPSAyMCkpKQoKCgpgYGAKCgojIyMjIFRvdGFsIE1pbnV0ZXMgQXNsZWVwCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicgfQojIENyZWF0ZSBhIGJveHBsb3QgZm9yIHRvdGFsX21pbnV0ZXNfYXNsZWVwCmJveHBsb3QoZGFpbHlfc2xlZXBfY2xlYW4kdG90YWxfbWludXRlc19hc2xlZXAsIAogICAgICAgIG1haW4gPSAiQm94cGxvdCBvZiBUb3RhbCBNaW51dGVzIEFzbGVlcCIsCiAgICAgICAgeWxhYiA9ICJUb3RhbCBNaW51dGVzIEFzbGVlcCIpCgojIENhbGN1bGF0ZSB0aGUgbWVkaWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24KbWVkaWFuX3ZhbHVlIDwtIG1lZGlhbihkYWlseV9zbGVlcF9jbGVhbiR0b3RhbF9taW51dGVzX2FzbGVlcCkKc3RkX2RldiA8LSByb3VuZChzZChkYWlseV9zbGVlcF9jbGVhbiR0b3RhbF9taW51dGVzX2FzbGVlcCksIDIpCgojIElkZW50aWZ5IG91dGxpZXJzCm91dGxpZXJzIDwtIGJveHBsb3Quc3RhdHMoZGFpbHlfc2xlZXBfY2xlYW4kdG90YWxfbWludXRlc19hc2xlZXApJG91dAoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIG91dGxpZXJzCm51bV9vdXRsaWVycyA8LSBsZW5ndGgob3V0bGllcnMpCgojIENyZWF0ZSB0aGUgbGVnZW5kIGxhYmVsIHdpdGggbWVkaWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBvdXRsaWVyIGNvdW50CmxlZ2VuZF9sYWJlbCA8LSBwYXN0ZSgiTWVkaWFuOiIsIG1lZGlhbl92YWx1ZSwgCiAgICAgICAgICAgICAgICAgICAgICAiXG5TdGFuZGFyZCBEZXZpYXRpb246Iiwgc3RkX2RldiwgCiAgICAgICAgICAgICAgICAgICAgICAiXG5PdXRsaWVyczoiLCBudW1fb3V0bGllcnMpCgojIEFkZCB0aGUgbGVnZW5kIHdpdGggbWVkaWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBvdXRsaWVyIGNvdW50CmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQgPSBsZWdlbmRfbGFiZWwsIHBjaCA9ICIiLCBjb2wgPSAiYmxhY2siLCBidHkgPSAibiIsIGNleCA9IDAuODUpCgpgYGAKYGBge3J9CiMgU2xlZXAgZHVyYXRpb24gYXZlcmFnZXMgYnkgSURzIHdpdGggc3RhbmRhcmQgZGV2aWF0aW9uIGFuZCBjb3VudCAobikKc2xlZXBfZGYgPC0gZGFpbHlfc2xlZXBfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoaWQpICU+JQogIHN1bW1hcmlzZShhdmVyYWdlX3NsZWVwX21pbnV0ZXMgPSBtZWFuKHRvdGFsX21pbnV0ZXNfYXNsZWVwKSwKICAgICAgICAgICAgc3RhbmRhcmRfZGV2aWF0aW9uX3NsZWVwX21pbnV0ZXMgPSBzZCh0b3RhbF9taW51dGVzX2FzbGVlcCksCiAgICAgICAgICAgIG4gPSBuKCkpCgpzbGVlcF9kZgoKCgpgYGAKCmBgYHtyfQojIERyb3AgSUQgIjIzMjAxMjcwMDIiIGR1ZSB0byBpbnN1ZmZpY2llbnQgZGF0YSBmb3IgY29tcHV0aW5nIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbi4Kc2xlZXBfZGYgPC0gc2xlZXBfZGYgJT4lIApmaWx0ZXIoaWQgIT0gIjIzMjAxMjcwMDIiKQpgYGAKCgoKYGBge3J9CnNsZWVwX2RmIApgYGAKCgpgYGB7cn0KIyBDYWxjdWxhdGUgcGVyY2VudGFnZXMgZm9yIHRoZSBhdmVyYWdlIGNvbHVtbgpiZWxvd182X2hvdXJzIDwtIHN1bShzbGVlcF9kZiRhdmVyYWdlX3NsZWVwX21pbnV0ZXMgPCAzNjApIC8gbnJvdyhzbGVlcF9kZikgKiAxMDAKYmV0d2Vlbl82XzdfaG91cnMgPC0gc3VtKHNsZWVwX2RmJGF2ZXJhZ2Vfc2xlZXBfbWludXRlcyA+PSAzNjAgJiBzbGVlcF9kZiRhdmVyYWdlX3NsZWVwX21pbnV0ZXMgPCA0MjApIC8gbnJvdyhzbGVlcF9kZikgKiAxMDAKYXRfbGVhc3RfN19ob3VycyA8LSBzdW0oc2xlZXBfZGYkYXZlcmFnZV9zbGVlcF9taW51dGVzID49IDQyMCkgLyBucm93KHNsZWVwX2RmKSAqIDEwMAoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGZvciB0aGUgc2xlZXAgZHVyYXRpb24gY2F0ZWdvcmllcwpwZXJjZW50YWdlX3NsZWVwX2RmIDwtIGRhdGEuZnJhbWUoCiAgQ2F0ZWdvcnkgPSBjKCJCZWxvdyA2IGhvdXJzIiwgIkJldHdlZW4gNiBhbmQgNyBob3VycyIsICJBdCBsZWFzdCA3IGhvdXJzIiksCiAgUGVyY2VudGFnZV9BdmVyYWdlID0gcm91bmQoYyhiZWxvd182X2hvdXJzLCBiZXR3ZWVuXzZfN19ob3VycywgYXRfbGVhc3RfN19ob3VycykpCikKCiMgQ29udmVydCBDYXRlZ29yeSB0byBhIGZhY3RvciB3aXRoIGN1c3RvbSBmYWN0b3IgbGV2ZWxzCnBlcmNlbnRhZ2Vfc2xlZXBfZGYkQ2F0ZWdvcnkgPC0gZmFjdG9yKHBlcmNlbnRhZ2Vfc2xlZXBfZGYkQ2F0ZWdvcnksIGxldmVscyA9IGMoIkJlbG93IDYgaG91cnMiLCAiQmV0d2VlbiA2IGFuZCA3IGhvdXJzIiwgIkF0IGxlYXN0IDcgaG91cnMiKSkKCnBlcmNlbnRhZ2Vfc2xlZXBfZGYKYGBgCgoKCmBgYHtyfQpzdHIocGVyY2VudGFnZV9zbGVlcF9kZikKYGBgCgoKCgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInIH0KCgpnZ3Bsb3QocGVyY2VudGFnZV9zbGVlcF9kZiwgYWVzKHggPSBDYXRlZ29yeSwgeSA9IFBlcmNlbnRhZ2VfQXZlcmFnZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJwdXJwbGUiKSArCiAgbGFicyh4ID0gIkF2ZXJhZ2UgU2xlZXAgRHVyYXRpb24iLCB5ID0gIlBlcmNlbnRhZ2Ugb2YgVXNlcnMiLCAKICAgICAgIHRpdGxlID0gIjUyJSBvZiBVc2VycyBHZXQgTGVzcyBUaGFuIDcgSG91cnMgb2YgU2xlZXAgb24gQXZlcmFnZSBEYWlseSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKFBlcmNlbnRhZ2VfQXZlcmFnZSwgIiUiKSksIHZqdXN0ID0gLTAuNSwgY29sb3IgPSAiYmxhY2siKSArIAogIHlsaW0oMCwgMTAwKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkKCmBgYAoKCgojIyMjIFNsZWVwIER1cmF0aW9uIENvbnNpc3RlbmN5CmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJyB9CiNFcnJvciBiYXJzCgojIENvbnZlcnQgYXZlcmFnZV9zbGVlcF9taW51dGVzIGFuZCBzdGFuZGFyZF9kZXZpYXRpb25fc2xlZXBfbWludXRlcyB0byBob3VycwpzbGVlcF9kZiRhdmVyYWdlX3NsZWVwX2hvdXJzIDwtIHNsZWVwX2RmJGF2ZXJhZ2Vfc2xlZXBfbWludXRlcyAvIDYwCnNsZWVwX2RmJHN0YW5kYXJkX2RldmlhdGlvbl9zbGVlcF9ob3VycyA8LSBzbGVlcF9kZiRzdGFuZGFyZF9kZXZpYXRpb25fc2xlZXBfbWludXRlcyAvIDYwCgoKIyBDcmVhdGUgYSBiYXIgcGxvdCBmb3IgZWFjaCAnaWQnIHdpdGggZXJyb3IgYmFycyByZXByZXNlbnRpbmcgc3RhbmRhcmQgZGV2aWF0aW9uCmdncGxvdChzbGVlcF9kZiwgYWVzKHggPSBpZCwgeSA9IGF2ZXJhZ2Vfc2xlZXBfaG91cnMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic2t5Ymx1ZSIsIGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBhdmVyYWdlX3NsZWVwX2hvdXJzIC0gc3RhbmRhcmRfZGV2aWF0aW9uX3NsZWVwX21pbnV0ZXMgLyA2MCwKICAgICAgICAgICAgICAgICAgICB5bWF4ID0gYXZlcmFnZV9zbGVlcF9ob3VycyArIHN0YW5kYXJkX2RldmlhdGlvbl9zbGVlcF9taW51dGVzIC8gNjApLAogICAgICAgICAgICAgICAgd2lkdGggPSAwLjIsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC45KSwgY29sb3IgPSAiYmxhY2siKSArCiAgbGFicyh4ID0gIklEIiwgeSA9ICJBdmVyYWdlIFNsZWVwIER1cmF0aW9uIChob3VycykiLAogICAgICAgdGl0bGUgPSAiU2xlZXAgQ29uc2lzdGVuY3k6IEF2ZXJhZ2UgU2xlZXAgRHVyYXRpb24gd2l0aCBFcnJvciBCYXJzIiwKICAgICAgIHN1YnRpdGxlID0gIkVycm9yIGJhcnMgcmVwcmVzZW50IHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gYXJvdW5kIHRoZSBtZWFuLiIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA3LCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMiwgMSkpICAjIEFkanVzdCB0aGUgcmFuZ2UgYXMgbmVlZGVkCgpgYGAKCgoKCmBgYHtyfQojIENhbGN1bGF0ZSBzbGVlcCBkdXJhdGlvbiBhdmVyYWdlcyBhbmQgc3RhbmRhcmQgZGV2aWF0aW9ucyBpbiBob3VycwpzbGVlcF9kZiA8LSBkYWlseV9zbGVlcF9jbGVhbiAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKG4gPSBuKCksCiAgICAgICAgICAgIGF2ZXJhZ2Vfc2xlZXBfaG91cnMgPSBtZWFuKHRvdGFsX21pbnV0ZXNfYXNsZWVwKSAvIDYwLCAgICAgICAjIENvbnZlcnQgbWludXRlcyB0byBob3VycwogICAgICAgICAgICBhdmVyYWdlX3RpbWVfaW5fYmVkX2hvdXJzID0gbWVhbih0b3RhbF90aW1lX2luX2JlZCkgLyA2MCwgICAKICAgICAgICAgICAgc3RhbmRhcmRfZGV2aWF0aW9uX3NsZWVwX2hvdXJzID0gc2QodG90YWxfbWludXRlc19hc2xlZXApIC8gNjAsICAgICAKICAgICAgICAgICAgc3RhbmRhcmRfZGV2aWF0aW9uX3RpbWVfaW5fYmVkX2hvdXJzID0gc2QodG90YWxfdGltZV9pbl9iZWQpIC8gNjAsICAgIAogICAgICAgICAgICkgJT4lCiAgbXV0YXRlKHRpbWVfZGlmZmVyZW5jZV9ob3VycyA9IGF2ZXJhZ2VfdGltZV9pbl9iZWRfaG91cnMgLSBhdmVyYWdlX3NsZWVwX2hvdXJzLCAgIyBDYWxjdWxhdGUgdGhlIHRpbWUgZGlmZmVyZW5jZSBpbiBob3VycwogICAgICAgICBhdmVyYWdlX2F3YWtlX2luX2JlZF9ob3VycyA9IHRpbWVfZGlmZmVyZW5jZV9ob3VycywgICMgUmVuYW1lIGNvbHVtbiAiYXdha2VfaW5fYmVkIgogICAgICAgICBzZF9hd2FrZV9pbl9iZWRfaG91cnMgPSBzZCh0aW1lX2RpZmZlcmVuY2VfaG91cnMpKSAgIyBDYWxjdWxhdGUgU0QgZm9yICJhd2FrZV9pbl9iZWQiIGluIGhvdXJzCgoKc2xlZXBfZGYKCgoKYGBgCgoKYGBge3J9CiMgRHJvcCBJRCAiMjMyMDEyNzAwMiIgZHVlIHRvIGluc3VmZmljaWVudCBkYXRhIGZvciBjb21wdXRpbmcgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uLgpzbGVlcF9kZiA8LSBzbGVlcF9kZiAlPiUgCmZpbHRlcihpZCAhPSAiMjMyMDEyNzAwMiIpCmRpbShzbGVlcF9kZikKYGBgCgoKCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicgfQpjcmVhdGVfYm94cGxvdHNfaW5fb25lX291dHB1dCA8LSBmdW5jdGlvbihkYXRhX2ZyYW1lLCBjb2x1bW5zX3RvX2FuYWx5emUsIGRlY2ltYWxfcGxhY2VzID0gMikgewogIG51bV9jb2x1bW5zIDwtIGxlbmd0aChjb2x1bW5zX3RvX2FuYWx5emUpCiAgbnVtX3Jvd3MgPC0gY2VpbGluZyhudW1fY29sdW1ucyAvIDIpCiAgCiAgcGFyKG1mcm93ID0gYyhudW1fcm93cywgMikpICAjIFNldCB0aGUgcGxvdHRpbmcgbGF5b3V0CiAgCiAgZm9yIChpIGluIDE6bnVtX2NvbHVtbnMpIHsKICAgIGNvbHVtbl9uYW1lIDwtIGNvbHVtbnNfdG9fYW5hbHl6ZVtpXQogICAgYm94cGxvdChkYXRhX2ZyYW1lW1tjb2x1bW5fbmFtZV1dLAogICAgICAgICAgICB5bGFiID0gY29sdW1uX25hbWUpCiAgICAKICAgIG1lZGlhbl92YWx1ZSA8LSBtZWRpYW4oZGF0YV9mcmFtZVtbY29sdW1uX25hbWVdXSkKICAgIHN0ZF9kZXYgPC0gcm91bmQoc2QoZGF0YV9mcmFtZVtbY29sdW1uX25hbWVdXSksIGRlY2ltYWxfcGxhY2VzKQogICAgb3V0bGllcnMgPC0gYm94cGxvdC5zdGF0cyhkYXRhX2ZyYW1lW1tjb2x1bW5fbmFtZV1dKSRvdXQKICAgIG51bV9vdXRsaWVycyA8LSBsZW5ndGgob3V0bGllcnMpCiAgICAKICAgIGxlZ2VuZF9sYWJlbCA8LSBwYXN0ZSgiTWVkaWFuOiIsIHJvdW5kKG1lZGlhbl92YWx1ZSwgZGVjaW1hbF9wbGFjZXMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICJcblNEOiIsIHN0ZF9kZXYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIlxuT3V0bGllcnM6IiwgbnVtX291dGxpZXJzKQogICAgCiAgICBsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gbGVnZW5kX2xhYmVsLCBwY2ggPSAiIiwgY29sID0gImJsYWNrIiwgYnR5ID0gIm4iLCBjZXggPSAwLjc1KQogIH0KICAKICBwYXIobWZyb3cgPSBjKDEsIDEpKSAgIyBSZXNldCB0aGUgcGxvdHRpbmcgbGF5b3V0IHRvIGRlZmF1bHQKfQoKIyBDb2x1bW5zIHRvIGFuYWx5emUKY29sdW1uc190b19hbmFseXplIDwtIGMoImF2ZXJhZ2Vfc2xlZXBfaG91cnMiLCAiYXZlcmFnZV9hd2FrZV9pbl9iZWRfaG91cnMiKQoKIyBDYWxsIHRoZSBmdW5jdGlvbiB0byBjcmVhdGUgYm94cGxvdHMgaW4gb25lIG91dHB1dApjcmVhdGVfYm94cGxvdHNfaW5fb25lX291dHB1dChzbGVlcF9kZiwgY29sdW1uc190b19hbmFseXplLCBkZWNpbWFsX3BsYWNlcyA9IDIpCgpgYGAKCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicgfQojIENvbHVtbnMgdG8gYW5hbHl6ZQpjb2x1bW5zX3RvX2FuYWx5emUgPC0gYygic3RhbmRhcmRfZGV2aWF0aW9uX3NsZWVwX2hvdXJzIiwgInNkX2F3YWtlX2luX2JlZF9ob3VycyIpCgojIENhbGwgdGhlIGZ1bmN0aW9uCmNyZWF0ZV9ib3hwbG90c19pbl9vbmVfb3V0cHV0KHNsZWVwX2RmLCBjb2x1bW5zX3RvX2FuYWx5emUsIGRlY2ltYWxfcGxhY2VzID0gMikKYGBgCgoKYGBge3J9CiNDb2x1bW5zIHdpdGggb3V0bGllcnMgdG8gcmVtb3ZlCmNvbHVtbnNfd2l0aF9vdXRsaWVycyA8LSBjKCJhdmVyYWdlX3NsZWVwX2hvdXJzIiwgImF2ZXJhZ2VfYXdha2VfaW5fYmVkX2hvdXJzIiwgInN0YW5kYXJkX2RldmlhdGlvbl9zbGVlcF9ob3VycyIpCgojIEZ1bmN0aW9uIHRvIHJlbW92ZSBvdXRsaWVycyBmcm9tIGEgY29sdW1uCnJlbW92ZV9vdXRsaWVycyA8LSBmdW5jdGlvbihkYXRhLCBjb2x1bW5fbmFtZSkgewogIG91dGxpZXJfYm91bmRzIDwtIGJveHBsb3Quc3RhdHMoZGF0YVtbY29sdW1uX25hbWVdXSkkb3V0CiAgZGF0YV9ub19vdXRsaWVyczwtIGRhdGFbIShkYXRhW1tjb2x1bW5fbmFtZV1dICVpbiUgb3V0bGllcl9ib3VuZHMpLCBdCiAgcmV0dXJuKGRhdGFfbm9fb3V0bGllcnMpCn0KCiMgTG9vcCB0aHJvdWdoIGVhY2ggY29sdW1uIGFuZCByZW1vdmUgb3V0bGllcnMKZm9yIChjb2wgaW4gY29sdW1uc193aXRoX291dGxpZXJzKSB7CiAgc2xlZXBfZGYgPC0gcmVtb3ZlX291dGxpZXJzKHNsZWVwX2RmLCBjb2wpCn0KCmBgYAoKCmBgYHtyfQpzbGVlcF9kZgpgYGAKCgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInIH0KIyBDaGVjayBpZiBvdXRsaWVycyB3ZXJlIHJlbW92ZWQKY29sdW1uc190b19hbmFseXplIDwtIGMoImF2ZXJhZ2Vfc2xlZXBfaG91cnMiLCAiYXZlcmFnZV9hd2FrZV9pbl9iZWRfaG91cnMiLCAic3RhbmRhcmRfZGV2aWF0aW9uX3NsZWVwX2hvdXJzIikKCgojIENhbGwgdGhlIGZ1bmN0aW9uIHRvIGNyZWF0ZSBib3hwbG90cyBpbiBvbmUgb3V0cHV0CmNyZWF0ZV9ib3hwbG90c19pbl9vbmVfb3V0cHV0KHNsZWVwX2RmLCBjb2x1bW5zX3RvX2FuYWx5emUsIGRlY2ltYWxfcGxhY2VzID0gMikKYGBgCgoKYGBge3J9CiNMZXQgdXMgZGl2aWRlIHRoZSB1c2VycyBpbnRvIGlycmVndWxhciBzbGVlcGVycyBhbmQgcmVndWxhciBzbGVlcGVycy4gV2Ugd2lsbCB1c2UgdGhlIDc1dGggcGVyY2VudGlsZSBhcyB0aGUgdGhyZXNob2xkIHRvIGRldGVybWluZSBpcnJlZ3VsYXIgc2xlZXBlcnMuIFRoZSByZXN0IHdpbGwgYmUgY29uc2lkZXJlZCByZWd1bGFyIHNsZWVwZXJzLgoKIyBEZWZpbmUgdGhlIFRocmVzaG9sZCAoZS5nLiwgdXNpbmcgdGhlIDc1dGggcGVyY2VudGlsZSkKdGhyZXNob2xkIDwtIHF1YW50aWxlKHNsZWVwX2RmJHN0YW5kYXJkX2RldmlhdGlvbl9zbGVlcF9ob3VycywgMC43NSkKCiMgQ3JlYXRlIGEgbmV3IGNvbHVtbiAic2xlZXBlcl90eXBlIiBiYXNlZCBvbiB0aGUgdGhyZXNob2xkCnNsZWVwX2RmJHNsZWVwZXJfdHlwZSA8LSBpZmVsc2Uoc2xlZXBfZGYkc3RhbmRhcmRfZGV2aWF0aW9uX3NsZWVwX2hvdXJzID4gdGhyZXNob2xkLCAiaXJyZWd1bGFyIiwgInJlZ3VsYXIiKQoKCmBgYAoKCgoKYGBge3J9CnNsZWVwX2RmCmBgYAoKCgpgYGB7cn0KIyBzbGVlcF90eXBlIGNvdW50cwp0YWJsZShzbGVlcF9kZiRzbGVlcGVyX3R5cGUpCgpgYGAKCmBgYHtyfQpzbGVlcF9kZgpgYGAKCgoKCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicgfQoKY29sb3Jfb3B0aW9ucyA8LSBjKCIjRTY5RjAwIiwgIiMwMDcyQjIiKSAjIEJsdWU6ICIjMDA3MkIyIiwgT3JhbmdlOiAiI0U2OUYwMCIKCiMgRnVuY3Rpb24gdG8gY3JlYXRlIHRoZSB2aW9saW4gcGxvdCBmb3IgYSBnaXZlbiB5LWF4aXMgY29sdW1uCmNyZWF0ZV92aW9saW5fcGxvdCA8LSBmdW5jdGlvbihkYXRhLCB4X2F4aXNfY29sLCB5X2F4aXNfY29sKSB7CiAgZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcoeCA9IHhfYXhpc19jb2wsIHkgPSB5X2F4aXNfY29sLCBmaWxsID0geF9heGlzX2NvbCkpICsKICAgIGdlb21fdmlvbGluKHNjYWxlID0gIndpZHRoIiwgZHJhd19xdWFudGlsZXMgPSBjKDAuMjUsIDAuNSwgMC43NSksIHRyaW0gPSBGQUxTRSkgKwogICAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4xLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKSArCiAgICBsYWJzKHggPSAiU2xlZXBlciBUeXBlIiwgeSA9IHlfYXhpc19jb2wsIHRpdGxlID0gcGFzdGUoIkNvbXBhcmlzb24iLHhfYXhpc19jb2wsImZvciIsIHlfYXhpc19jb2wpKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcl9vcHRpb25zKSArCiAgICB0aGVtZV9taW5pbWFsKCkKfQoKCmBgYAoKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJyB9CiMgQ2FsbCB0aGUgZnVuY3Rpb24gdG8gY3JlYXRlIHRoZSB2aW9saW4gcGxvdHMgZm9yIGVhY2ggY29sdW1uCmZvciAoY29sIGluIGMoImF2ZXJhZ2Vfc2xlZXBfaG91cnMiLCAic3RhbmRhcmRfZGV2aWF0aW9uX3NsZWVwX2hvdXJzIiwgImF2ZXJhZ2VfYXdha2VfaW5fYmVkX2hvdXJzIiwic2RfYXdha2VfaW5fYmVkX2hvdXJzIikpIHsKICBwbG90IDwtIGNyZWF0ZV92aW9saW5fcGxvdChzbGVlcF9kZiwgInNsZWVwZXJfdHlwZSIsIGNvbCkKICBwcmludChwbG90KQp9CgpgYGAKT2JzZXJ2YXRpb25zOgoKLSBSZWd1bGFyIHNsZWVwZXJzIHRlbmQgdG8gaGF2ZSBoaWdoZXIgbWVkaWFuIGF2ZXJhZ2Ugc2xlZXAgaG91cnMgY29tcGFyZWQgdG8gaXJyZWd1bGFyIHNsZWVwZXJzLlRoaXMgc3VnZ2VzdHMgdGhhdCBpbmRpdmlkdWFscyBjbGFzc2lmaWVkIGFzIHJlZ3VsYXIgc2xlZXBlcnMgYXJlIGxpa2VseSBnZXR0aW5nIG1vcmUgc2xlZXAgb24gYXZlcmFnZSB0aGFuIHRob3NlIGNhdGVnb3JpemVkIGFzIGlycmVndWxhciBzbGVlcGVycy4KCi0gQWRkaXRpb25hbGx5LCB0aGUgc3ByZWFkIG9mIHRoZSAiYXZlcmFnZV9zbGVlcF9ob3VycyIgZm9yIGlycmVndWxhciBzbGVlcGVycyBhcHBlYXJzIHRvIGJlIHdpZGVyLCBpbmRpY2F0aW5nIG1vcmUgdmFyaWFiaWxpdHkgaW4gdGhlaXIgc2xlZXAgZHVyYXRpb24uIEluIGNvbnRyYXN0LCB0aGUgdmlvbGluIHBsb3QgZm9yIHJlZ3VsYXIgc2xlZXBlcnMgc2hvd3MgYSBuYXJyb3dlciBzcHJlYWQsIHN1Z2dlc3RpbmcgdGhhdCB0aGVpciBzbGVlcCBkdXJhdGlvbiBpcyBtb3JlIGNvbnNpc3RlbnQuCgotIFJlZ3VsYXIgc2xlZXBlcnMgZXhoaWJpdCBhIHNsaWdodGx5IGhpZ2hlciBtZWRpYW4gYXZlcmFnZSBhd2FrZS1pbi1iZWQgZHVyYXRpb24gY29tcGFyZWQgdG8gaXJyZWd1bGFyIHNsZWVwZXJzLgoKU3VtbWFyeTogUmVndWxhciBzbGVlcGVycyBnZXQgbW9yZSBzbGVlcCBvbiBhdmVyYWdlLCBoYXZlIGEgbW9yZSBjb25zaXN0ZW50IHNsZWVwIGR1cmF0aW9uLCBhbmQgc2xpZ2h0bHkgaGlnaGVyIG1lZGlhbiBhd2FrZS1pbi1iZWQgZHVyYXRpb24gdGhhbiBpcnJlZ3VsYXIgc2xlZXBlcnMuCgoKCiMjIyMgRURBIG1pbnV0ZV9zbGVlcF9jbGVhbgpgYGB7cn0Kc3RyKG1pbnV0ZV9zbGVlcF9jbGVhbikKYGBgClRoaXMgZGF0YSBzZWVtcyB0byBjb21lIGZyb20gdGhlIENsYXNzaWMgU2xlZXAgTG9nICgxIG1pbnV0ZSkKClZhbHVlIGluZGljYXRpbmcgdGhlIHNsZWVwIHN0YXRlLgoxID0gYXNsZWVwLCAyID0gcmVzdGxlc3MsIDMgPSBhd2FrZQoKCkZvciBtb3JlIGRldGFpbCBjaGVjayA6ICBbRml0Yml0IGRhdGEgZGljdGlvbmFyeSBdKGh0dHBzOi8vd3d3LmZpdGFiYXNlLmNvbS9yZXNvdXJjZXMva25vd2xlZGdlLWJhc2UvZXhwb3J0aW5nLWRhdGEvZGF0YS1kaWN0aW9uYXJpZXMvKQoKCgpgYGB7cn0KIyBBZGQgbGFiZWxzIHRvIHRoZSB2ZWx1ZSBjb2x1bW4KbWludXRlX3NsZWVwX2NsZWFuJHZhbHVlIDwtIGZhY3RvcihtaW51dGVfc2xlZXBfY2xlYW4kdmFsdWUsIGxldmVscyA9IGMoIjEiLCAiMiIsICIzIiksIGxhYmVscyA9IGMoImFzbGVlcCIsICJyZXN0bGVzcyIsICJhd2FrZSIpKQpgYGAKCgpgYGB7cn0KbWludXRlX3NsZWVwX2NsZWFuICU+JSBzdW1tYXJ5KCkKYGBgCgpgYGB7cn0KCiMgQXNzdW1pbmcgInZhbHVlIiBjb2x1bW4gcmVwcmVzZW50cyB0b3RhbCBzbGVlcCByZWNvcmRzCmZyZXF1ZW5jeV90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKG1pbnV0ZV9zbGVlcF9jbGVhbiR2YWx1ZSkpCmZyZXF1ZW5jeV90YWJsZSRQZXJjZW50YWdlIDwtIGZyZXF1ZW5jeV90YWJsZSRGcmVxIC8gc3VtKGZyZXF1ZW5jeV90YWJsZSRGcmVxKSAqIDEwMAoKZ2dwbG90KGRhdGEgPSBmcmVxdWVuY3lfdGFibGUsIGFlcyh4ID0gVmFyMSwgeSA9IEZyZXEpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiIzAwODA4MCIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUoRnJlcSwgIiAoIiwgcGVyY2VudChQZXJjZW50YWdlIC8gMTAwKSwgIikiLCBzZXAgPSAiIikpLAogICAgICAgICAgICBoanVzdCA9IDAuNSwgIHZqdXN0ID0gLTAuNCwgY29sb3IgPSAiYmxhY2siKSArCiAgbGFicyh4ID0gIlRvdGFsIE1pbnV0ZXMgUmVjb3JkcyIsIHkgPSAiRnJlcXVlbmN5IiwKICAgICAgIHRpdGxlID0gIlVzZXIgU2xlZXAgU3RhdGVzOiA5MSUgb2YgTWludXRlcyBTcGVudCBBc2xlZXAgd2l0aCBNaW5pbWFsIEludGVycnVwdGlvbnM6IiwKICAgICAgIHN1YnRpdGxlID0gIlJlc3RsZXNzbmVzczogNy40JSB8IEF3YWtlOiAxLjElIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIG1hcmdpbiA9IG1hcmdpbihiID0gMjApKSkKCmBgYAoKCgoKIyMgIEVEQSBmb3IgaG91cmx5X2FjdGl2aXR5X2NsZWFuCgpgYGB7cn0Kc3RyKGhvdXJseV9hY3Rpdml0eV9jbGVhbikKYGBgCi0gQ2Fsb3JpZXMgaW50ZWdlciBUb3RhbCBudW1iZXIgb2YgZXN0aW1hdGVkIGNhbG9yaWVzIGJ1cm5lZC4KCi0gVG90YWxJbnRlbnNpdHk6IGludGVnZXIgVmFsdWUgY2FsY3VsYXRlZCBieSBhZGRpbmcgYWxsIHRoZSBtaW51dGUtbGV2ZWwKaW50ZW5zaXR5IHZhbHVlcyB0aGF0IG9jY3VycmVkIHdpdGhpbiB0aGUgaG91ci4KCi0gQXZlcmFnZUludGVuc2l0eTogaW50ZW5zaXR5IHN0YXRlIGV4aGliaXRlZCBkdXJpbmcgdGhhdApob3VyIChUb3RhbEludGVuc2l0eSBmb3IgdGhhdCBBY3Rpdml0eUhvdXIgZGl2aWRlZCBieSA2MCkKCi0gU3RlcFRvdGFsOiBUb3RhbCBudW1iZXIgb2Ygc3RlcHMgdGFrZW4uCgpGb3IgbW9yZSBkZXRhaWwgY2hlY2sgOiAgW0ZpdGJpdCBkYXRhIGRpY3Rpb25hcnkgXShodHRwczovL3d3dy5maXRhYmFzZS5jb20vcmVzb3VyY2VzL2tub3dsZWRnZS1iYXNlL2V4cG9ydGluZy1kYXRhL2RhdGEtZGljdGlvbmFyaWVzLykKCmBgYHtyfQpob3VybHlfZGYgPC1ob3VybHlfYWN0aXZpdHlfY2xlYW4KCgojIEV4dHJhY3QgImFtIiBvciAicG0iIGZyb20gdGhlIGFjdGl2aXR5X2hvdXIgY29sdW1uCmhvdXJseV9kZiRhbV9wbSA8LSBpZmVsc2UoZm9ybWF0KGhvdXJseV9kZiRhY3Rpdml0eV9ob3VyLCAiJXAiKSA9PSAiQU0iLCAiYW0iLCAicG0iKQoKI0FkZCBhIGNvbHVtbiBmb3IgdGhlIGhvdXIKaG91cmx5X2RmJGhvdXIgPC0gZm9ybWF0KGhvdXJseV9kZiRhY3Rpdml0eV9ob3VyLCAiJUgiKQoKYGBgCgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInIH0KIyBEZWZpbmUgY29sb3JzIGZvciBBTSBhbmQgUE0KY29sb3JfcGFsZXR0ZSA8LSBjKCIjRkZBNTAwIiwgIiNBREQ4RTYiKSAgIyBPcmFuZ2UgZm9yIEFNLCBMaWdodCBCbHVlIGZvciBQTQoKIyBDdXN0b20gZnVuY3Rpb24gdG8gZ2VuZXJhdGUgdGhlIGJveHBsb3Qgd2l0aCBkeW5hbWljYWxseSBzZXQgeS1heGlzIGxpbWl0cwpnZW5lcmF0ZV9ib3hwbG90IDwtIGZ1bmN0aW9uKGRhdGEsIHlfdmFyLCB5X2xhYmVsLCBsaW1pdF9mYWN0b3IpIHsKICB5X2xpbWl0IDwtIHF1YW50aWxlKGRhdGFbW3lfdmFyXV0sIDAuOTUpICogbGltaXRfZmFjdG9yCiAgCiAgZ2dwbG90KGRhdGEsIGFlcyh4ID0gaG91ciwgeSA9IGdldCh5X3ZhciksIGZpbGwgPSBhbV9wbSkpICsKICAgIGdlb21fYm94cGxvdChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksIG91dGxpZXIuc2hhcGUgPSBOQSkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JfcGFsZXR0ZSkgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJNZWRpYW4iLCB5X2xhYmVsLCAiYnkgSG91ciIpLAogICAgICAgICB4ID0gIkhvdXIiLAogICAgICAgICB5ID0gcGFzdGUoIk1lZGlhbiIsIHlfbGFiZWwpKSArCiAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9IE5VTEwpKSArICAjIFJlbW92ZSBsZWdlbmQgdGl0bGUKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICAgIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCB5X2xpbWl0KSkKfQoKIyBBc3N1bWluZyB5b3VyIGRhdGFzZXQgaXMgbmFtZWQgJ2hvdXJseV9kZjEnCmRhdGEgPC0gaG91cmx5X2RmCgojIENyZWF0ZSB0aGUgcGxvdHMgd2l0aCBkeW5hbWljYWxseSBhZGp1c3RlZCB5LWF4aXMgbGltaXRzCiMgQWRqdXN0IHRoZSBsaW1pdF9mYWN0b3IgYXMgbmVlZGVkIChlLmcuLCAxLjEsIDEuMiwgZXRjLikKY2Fsb3JpZXNfcGxvdCA8LSBnZW5lcmF0ZV9ib3hwbG90KGRhdGEsICJjYWxvcmllcyIsICJDYWxvcmllIEJ1cm4iLCBsaW1pdF9mYWN0b3IgPSAxLjQpCnRvdGFsX2ludGVuc2l0eV9wbG90IDwtIGdlbmVyYXRlX2JveHBsb3QoZGF0YSwgInRvdGFsX2ludGVuc2l0eSIsICJUb3RhbCBJbnRlbnNpdHkiLCBsaW1pdF9mYWN0b3IgPSAxLjMpCnN0ZXBfdG90YWxfYXZnX3Bsb3QgPC0gZ2VuZXJhdGVfYm94cGxvdChkYXRhLCAic3RlcF90b3RhbCIsICJUb3RhbCBTdGVwcyIsIGxpbWl0X2ZhY3RvciA9IDEuMykKCiMgUHJpbnQgdGhlIHBsb3RzCnByaW50KGNhbG9yaWVzX3Bsb3QpCnByaW50KHRvdGFsX2ludGVuc2l0eV9wbG90KQpwcmludChzdGVwX3RvdGFsX2F2Z19wbG90KQpgYGAKCgoKCgoKCmBgYHtyIH0KCiMgR3JvdXAgYnkgaWQsIGhvdXIsIGFuZCBhbV9wbSBhbmQgc3VtbWFyaXplIHRoZSBjb2x1bW5zCnN1bW1hcnlfZGF0YSA8LSBob3VybHlfZGYgJT4lCiAgZ3JvdXBfYnkoaWQsIGhvdXIsIGFtX3BtKSAlPiUKICBzdW1tYXJpemUoCiAgICBjYWxvcmllc19hdmcgPSBtZWFuKGNhbG9yaWVzKSwKICAgIGNhbG9yaWVzX21heCA9IG1heChjYWxvcmllcyksCiAgICBjYWxvcmllc19taW4gPSBtaW4oY2Fsb3JpZXMpLAogICAgdG90YWxfaW50ZW5zaXR5X2F2ZyA9IG1lYW4odG90YWxfaW50ZW5zaXR5KSwKICAgIHRvdGFsX2ludGVuc2l0eV9tYXggPSBtYXgodG90YWxfaW50ZW5zaXR5KSwKICAgIHRvdGFsX2ludGVuc2l0eV9taW4gPSBtaW4odG90YWxfaW50ZW5zaXR5KSwKICAgIGF2ZXJhZ2VfaW50ZW5zaXR5X2F2ZyA9IG1lYW4oYXZlcmFnZV9pbnRlbnNpdHkpLAogICAgc3RlcF90b3RhbF9hdmcgPSBtZWFuKHN0ZXBfdG90YWwpLAogICAgb2JzZXJ2YXRpb25zX2NvdW50ID0gbigpCiAgKQoKCgpgYGAKCgoKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30KCgojIERlZmluZSBjb2xvcnMgZm9yIEFNIGFuZCBQTQpjb2xvcl9wYWxldHRlIDwtIGMoIiNGRkE1MDAiLCAiI0FERDhFNiIpICAjIE9yYW5nZSBmb3IgQU0sIExpZ2h0IEJsdWUgZm9yIFBNCgojIEN1c3RvbSBmdW5jdGlvbiB0byBnZW5lcmF0ZSB0aGUgYmFyIHBsb3Qgd2l0aCBkeW5hbWljYWxseSBzZXQgeS1heGlzIGxpbWl0cwpnZW5lcmF0ZV9iYXJfcGxvdCA8LSBmdW5jdGlvbihkYXRhLCB5X3ZhciwgeV9sYWJlbCwgbGltaXRfZmFjdG9yKSB7CiAgeV9saW1pdCA8LSBtYXgoZGF0YVtbcGFzdGUwKHlfdmFyLCAiX2F2ZyIpXV0pICogbGltaXRfZmFjdG9yCiAgCiAgZ2dwbG90KGRhdGEsIGFlcyh4ID0gaG91ciwgeSA9IGdldChwYXN0ZTAoeV92YXIsICJfYXZnIikpLCBmaWxsID0gYW1fcG0pKSArCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcl9wYWxldHRlKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUoIkF2ZXJhZ2UiLCB5X2xhYmVsLCAiYnkgSG91ciIpLAogICAgICAgICB4ID0gIkhvdXIiLAogICAgICAgICB5ID0gcGFzdGUoIkF2ZXJhZ2UiLCB5X2xhYmVsKSkgKwogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGUgPSBOVUxMKSkgKyAgIyBSZW1vdmUgbGVnZW5kIHRpdGxlCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwgeV9saW1pdCkpCn0KCgojIEFzc3VtaW5nIHlvdXIgZGF0YXNldCBpcyBuYW1lZCAnc3VtbWFyeV9kYXRhJwpkYXRhIDwtIHN1bW1hcnlfZGF0YQoKIyBDcmVhdGUgdGhlIGJhciBwbG90cyB3aXRoIGR5bmFtaWNhbGx5IGFkanVzdGVkIHktYXhpcyBsaW1pdHMKY2Fsb3JpZXNfcGxvdCA8LSBnZW5lcmF0ZV9iYXJfcGxvdChkYXRhLCAiY2Fsb3JpZXMiLCAiQ2Fsb3JpZSBCdXJuIiwgbGltaXRfZmFjdG9yID0gMS4yKQp0b3RhbF9pbnRlbnNpdHlfcGxvdCA8LSBnZW5lcmF0ZV9iYXJfcGxvdChkYXRhLCAidG90YWxfaW50ZW5zaXR5IiwgIlRvdGFsIEludGVuc2l0eSIsIGxpbWl0X2ZhY3RvciA9IDEuMikKc3RlcHNfcGxvdCA8LSBnZW5lcmF0ZV9iYXJfcGxvdChkYXRhLCAic3RlcF90b3RhbCIsICJTdGVwcyBUYWtlbiIsIGxpbWl0X2ZhY3RvciA9IDEuMSkKCiMgUHJpbnQgdGhlIHBsb3RzCnByaW50KGNhbG9yaWVzX3Bsb3QpCnByaW50KHRvdGFsX2ludGVuc2l0eV9wbG90KQpwcmludChzdGVwc19wbG90KQoKYGBgCgoKYGBge3J9CiMgRnVuY3Rpb24gdG8gY3JlYXRlIGEgYm94IHBsb3Qgd2l0aCBjdXN0b21pemFibGUgb3JpZW50YXRpb24gYW5kIGNvbG9ycyBmb3IgYSBnaXZlbiB5LWF4aXMgY29sdW1uCmNyZWF0ZV9jdXN0b21fYm94X3Bsb3QgPC0gZnVuY3Rpb24oZGF0YSwgeF9heGlzX2NvbCwgeV9heGlzX2NvbCwgb3JpZW50YXRpb24gPSAidmVyIiwgY29sb3JzKSB7CiAgZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcoeCA9IHhfYXhpc19jb2wsIHkgPSB5X2F4aXNfY29sLCBmaWxsID0geF9heGlzX2NvbCkpICsKICAgIGdlb21fYm94cGxvdCh3aWR0aCA9IGlmZWxzZShvcmllbnRhdGlvbiA9PSAidmVyIiwgMC4yLCAwLjUpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikgKyAgIyBSZW1vdmUgZmlsbCA9ICJ3aGl0ZSIKICAgIGxhYnMoeCA9ICIiLCB5ID0geV9heGlzX2NvbCwKICAgICAgICAgdGl0bGUgPSBwYXN0ZSgiQ29tcGFyaXNvbiIsIHhfYXhpc19jb2wsICJmb3IiLCB5X2F4aXNfY29sKSkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JzKSArCiAgICAgZ3VpZGVzKGZpbGwgPSAibm9uZSIpICsgICMgUmVtb3ZlIHRoZSBsZWdlbmQgZm9yIGZpbGwgY29sb3IKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBpZiAob3JpZW50YXRpb24gPT0gImhvciIpIGNvb3JkX2ZsaXAoKQp9CgojIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggY3VzdG9tIGNvbG9ycyBhcyBhIHR1cGxlIGFuZCBzcGVjaWZ5IHRoZSB4LWF4aXMgbGFiZWwKY3VzdG9tX2NvbG9ycyA8LSBjKCIjRkZBNTAwIiwgIiNBREQ4RTYiKSAgIyBPcmFuZ2UgZm9yIEFNLCBMaWdodCBCbHVlIGZvciBQTQoKCmZvciAoY29sIGluIGMoImNhbG9yaWVzIiwgInRvdGFsX2ludGVuc2l0eSIsICJzdGVwX3RvdGFsIikpIHsKICBwbG90IDwtIGNyZWF0ZV9jdXN0b21fYm94X3Bsb3QoaG91cmx5X2RmLCB4X2F4aXNfY29sID0gImFtX3BtIiwgeV9heGlzX2NvbCA9IGNvbCwgb3JpZW50YXRpb24gPSAiaG9yIiwgY29sb3JzID0gY3VzdG9tX2NvbG9ycykKICBwcmludChwbG90KQp9CgoKYGBgCgpXaXRoIHRoZSBjb2RlIHByb3ZpZGVkLCB5b3UgY2FuIGdhaW4gc2V2ZXJhbCBpbnNpZ2h0cyBpbnRvIHVzZXIgYmVoYXZpb3IgYW5kIGFjdGl2aXR5IHBhdHRlcm5zIGJhc2VkIG9uIHRoZSAiaG91cmx5X2RmIiBkYXRhc2V0LiBTb21lIG9mIHRoZSBpbnNpZ2h0cyB5b3UgY2FuIG9idGFpbiBhcmU6CgpIb3VybHkgQWN0aXZpdHkgUGF0dGVybnM6IFlvdSBjYW4gb2JzZXJ2ZSBob3cgdXNlciBhY3Rpdml0eSB2YXJpZXMgdGhyb3VnaG91dCB0aGUgZGF5LiBUaGUgY2Fsb3JpZXNfYXZnLCB0b3RhbF9pbnRlbnNpdHlfYXZnLCBhbmQgYXZlcmFnZV9pbnRlbnNpdHlfYXZnIGNvbHVtbnMgd2lsbCBnaXZlIHlvdSB0aGUgYXZlcmFnZSBjYWxvcmllIGJ1cm4sIHRvdGFsIGludGVuc2l0eSwgYW5kIGF2ZXJhZ2UgaW50ZW5zaXR5IGZvciBlYWNoIGhvdXIsIHJlc3BlY3RpdmVseS4gVGhpcyBjYW4gaGVscCBpZGVudGlmeSBwZWFrIGFjdGl2aXR5IGhvdXJzIGFuZCBwZXJpb2RzIG9mIGxvd2VyIGFjdGl2aXR5LgoKVmFyaWFiaWxpdHkgaW4gQWN0aXZpdHk6IFRoZSBjYWxvcmllc19tYXgsIGNhbG9yaWVzX21pbiwgdG90YWxfaW50ZW5zaXR5X21heCwgYW5kIHRvdGFsX2ludGVuc2l0eV9taW4gY29sdW1ucyB3aWxsIHByb3ZpZGUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIG1heGltdW0gYW5kIG1pbmltdW0gdmFsdWVzIG9mIGNhbG9yaWUgYnVybiBhbmQgdG90YWwgaW50ZW5zaXR5IHJlY29yZGVkIGR1cmluZyBlYWNoIGhvdXIuIFRoaXMgY2FuIGhlbHAgeW91IHVuZGVyc3RhbmQgdGhlIHJhbmdlIG9mIGFjdGl2aXR5IGxldmVscyBhbmQgaG93IG11Y2ggdGhlIGFjdGl2aXR5IHZhcmllcyBmcm9tIGhvdXIgdG8gaG91ci4KCkF2ZXJhZ2UgU3RlcHMgcGVyIEhvdXI6IFRoZSBzdGVwX3RvdGFsX2F2ZyBjb2x1bW4gd2lsbCBnaXZlIHlvdSB0aGUgYXZlcmFnZSBudW1iZXIgb2Ygc3RlcHMgdGFrZW4gZHVyaW5nIGVhY2ggaG91ci4gVGhpcyBjYW4gaGVscCB5b3UgaWRlbnRpZnkgdGhlIHR5cGljYWwgc3RlcCBjb3VudCBkdXJpbmcgZGlmZmVyZW50IHRpbWVzIG9mIHRoZSBkYXkuCgpPYnNlcnZhdGlvbnMgQ291bnQ6IFRoZSBvYnNlcnZhdGlvbnNfY291bnQgY29sdW1uIHdpbGwgc2hvdyB0aGUgbnVtYmVyIG9mIGRhdGEgcG9pbnRzIChvYnNlcnZhdGlvbnMpIGF2YWlsYWJsZSBmb3IgZWFjaCAiaWQsIiAiaG91ciwiIGFuZCAiYW1fcG0iIGdyb3VwLiBUaGlzIGNhbiBoZWxwIHlvdSBhc3Nlc3MgdGhlIGRhdGEgZGVuc2l0eSBhbmQgaWRlbnRpZnkgaG91cnMgd2l0aCBtb3JlIG9yIGZld2VyIGRhdGEgcG9pbnRzLCB3aGljaCBtYXkgaW5mbHVlbmNlIHRoZSByZWxpYWJpbGl0eSBvZiB0aGUgaW5zaWdodHMuCgpBTSB2cy4gUE0gQWN0aXZpdHk6IFRoZSAiYW1fcG0iIGNvbHVtbiBpbmRpY2F0ZXMgd2hldGhlciB0aGUgYWN0aXZpdHkgb2NjdXJyZWQgZHVyaW5nIHRoZSBtb3JuaW5nIChBTSkgb3IgYWZ0ZXJub29uL2V2ZW5pbmcgKFBNKS4gWW91IGNhbiBjb21wYXJlIHRoZSBhY3Rpdml0eSBwYXR0ZXJucyBiZXR3ZWVuIHRoZXNlIHR3byBwZXJpb2RzIGFuZCBleHBsb3JlIGFueSBkaWZmZXJlbmNlcyBpbiB1c2VyIGJlaGF2aW9yIGR1cmluZyB0aGVzZSB0aW1lcy4KCkluZGl2aWR1YWwgVXNlciBJbnNpZ2h0czogQnkgZ3JvdXBpbmcgdGhlIGRhdGEgYnkgImlkLCIgeW91IGNhbiBhbHNvIGdhaW4gaW5zaWdodHMgaW50byBlYWNoIGluZGl2aWR1YWwgdXNlcidzIGFjdGl2aXR5IHBhdHRlcm5zLiBZb3UgY2FuIGFzc2VzcyB0aGVpciBhdmVyYWdlIGNhbG9yaWUgYnVybiwgaW50ZW5zaXR5LCBhbmQgc3RlcHMgZHVyaW5nIHZhcmlvdXMgaG91cnMuCgpPdmVyYWxsLCB0aGVzZSBpbnNpZ2h0cyBjYW4gaGVscCB5b3UgdW5kZXJzdGFuZCBob3cgdXNlcnMgZW5nYWdlIGluIHBoeXNpY2FsIGFjdGl2aXR5IHRocm91Z2hvdXQgdGhlIGRheSwgaWRlbnRpZnkgcGVhayBhY3Rpdml0eSBob3VycywgYW5kIGRldGVjdCBhbnkgcGF0dGVybnMgb3IgdHJlbmRzIGluIHRoZWlyIGJlaGF2aW9yLiBUaGlzIGluZm9ybWF0aW9uIGNhbiBiZSB2YWx1YWJsZSBmb3IgZGVzaWduaW5nIHBlcnNvbmFsaXplZCBmaXRuZXNzIHBsYW5zLCBvcHRpbWl6aW5nIGFjdGl2aXR5IHByb2dyYW1zLCBhbmQgbWFraW5nIGRhdGEtZHJpdmVuIGRlY2lzaW9ucyB0byBpbXByb3ZlIGhlYWx0aCBhbmQgd2VsbC1iZWluZy4KCgoKCiMjICBFREEgZm9yIHNlY29uZHNfaGVhcnRyYXRlX2NsZWFuCgoKYGBge3J9CnN0cihzZWNvbmRzX2hlYXJ0cmF0ZV9jbGVhbikKYGBgCgpgYGB7cn0Kc2Vjb25kc19oZWFydHJhdGVfY2xlYW4gJT4lIHN1bW1hcnkoKQpgYGAKYGBge3J9Cm5fZGlzdGluY3Qoc2Vjb25kc19oZWFydHJhdGVfY2xlYW4kaWQpCmBgYAoKCgoKYGBge3J9CgoKIyBHcm91cCBieSAnaWQnIGFuZCBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgaGVhcnQgcmF0ZSBmb3IgZWFjaCB1c2VyCmF2ZXJhZ2VfaGVhcnRfcmF0ZSA8LSBzZWNvbmRzX2hlYXJ0cmF0ZV9jbGVhbiAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKGF2ZXJhZ2VfaGVhcnRfcmF0ZSA9IG1lYW4oaGVhcnRfcmF0ZSwgbmEucm0gPSBUUlVFKSkKCiMgQmFyIFBsb3Qgd2l0aCBzbWFsbGVyIGJhcnMgYW5kIGN1c3RvbSBicmVha3Mgb24gdGhlIGhlYXJ0IHJhdGUgYXhpcwpiYXJfcGxvdCA8LSBnZ3Bsb3QoYXZlcmFnZV9oZWFydF9yYXRlLCBhZXMoeCA9IGlkLCB5ID0gYXZlcmFnZV9oZWFydF9yYXRlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIiNBREQ4RTYiLCB3aWR0aCA9IDAuOCkgKyAgIyBBZGp1c3QgdGhlIHdpZHRoIGhlcmUgKGUuZy4sIDAuNSBmb3Igc21hbGxlciBiYXJzKQogIGxhYnMoeCA9ICJVc2VyIElEIiwgeSA9ICJBdmVyYWdlIEhlYXJ0IFJhdGUiLCB0aXRsZSA9ICJBdmVyYWdlIEhlYXJ0IFJhdGUgZm9yIEVhY2ggVXNlciIpICsKICB0aGVtZV9taW5pbWFsKCkgKyBjb29yZF9mbGlwKCkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCAxMCksIGxpbWl0cyA9IGMoMCwgMTAwKSkrICMgU2V0IGN1c3RvbSBicmVha3MgYW5kIGxpbWl0cyBmb3IgdGhlIHktYXhpcyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA2MCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JlZW4iKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMTAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmVlbiIpIAoKIyBEaXNwbGF5IHRoZSBiYXIgcGxvdApwcmludChiYXJfcGxvdCkKCgpgYGAKCiMjICBFREEgZm9yIHdlaWdodF9sb2dzX2NsZWFuCgoKYGBge3J9CnN0cih3ZWlnaHRfbG9nc19jbGVhbikKYGBgCgotIEZhdDpCb2R5IGZhdCBwZXJjZW50YWdlIHJlY29yZGVkLgoKLSBCTUk6IE1lYXN1cmUgb2YgYm9keSBtYXNzIGluZGV4IGJhc2VkIG9uIHRoZSBoZWlnaHQgYW5kIHdlaWdodCBpbiB0aGUgcGFydGljaXBhbnTigJlzIEZpdGJpdC5jb20gcHJvZmlsZS4KCi0gaXNNYW51YWxSZXBvcnQ6IElmIHRoZSBkYXRhIGZvciB0aGlzIHdlaWdoLWluIHdhcyBkb25lIG1hbnVhbGx5IChUUlVFKSwgb3IgaWYgZGF0YSB3YXMgbWVhc3VyZWQgYW5kIHN5bmNlZCBkaXJlY3RseSB0byBGaXRiaXQuY29tIGZyb20gYSBjb25uZWN0ZWQgc2NhbGUgKEZBTFNFKS4KCgpGb3IgbW9yZSBkZXRhaWwgY2hlY2sgOiAgW0ZpdGJpdCBkYXRhIGRpY3Rpb25hcnkgXShodHRwczovL3d3dy5maXRhYmFzZS5jb20vcmVzb3VyY2VzL2tub3dsZWRnZS1iYXNlL2V4cG9ydGluZy1kYXRhL2RhdGEtZGljdGlvbmFyaWVzLykKCgoKCmBgYHtyfQp3ZWlnaHRfbG9nc19jbGVhbiAlPiUgc3VtbWFyeSgpCmBgYAoKYGBge3J9CiMgQ3JlYXRlIGJveHBsb3RzIGZvciAiYm1pIiBhbmQgIndlaWdodF9wb3VuZHMiCgpjb2x1bW5zX3RvX2FuYWx5emUgPC0gYygiYm1pIiwgIndlaWdodF9wb3VuZHMiKQoKIyBDYWxsIHRoZSBmdW5jdGlvbiB0byBjcmVhdGUgYm94cGxvdHMgCmNyZWF0ZV9ib3hwbG90c19pbl9vbmVfb3V0cHV0KHdlaWdodF9sb2dzX2NsZWFuLCBjb2x1bW5zX3RvX2FuYWx5emUsIGRlY2ltYWxfcGxhY2VzID0gMikKCmBgYAoKCmBgYHtyfQoKZW50cnlfY291bnQgPC0gd2VpZ2h0X2xvZ3NfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoaWQsIGlzX21hbnVhbF9yZXBvcnQpICU+JQogIHN1bW1hcml6ZShlbnRyeV9jb3VudCA9IG4oKSwgLmdyb3VwcyA9ICJrZWVwIikgJT4lCiAgYXJyYW5nZSAoLSBlbnRyeV9jb3VudCkKCnByaW50KGVudHJ5X2NvdW50KQoKCmBgYAoKCmBgYHtyfQojIENoZWNrIHVzZXJzIHRoYXQgcmVwb3J0ZWQgZmF0IHBlcmNlbnRhZ2UKd2VpZ2h0X2xvZ3NfY2xlYW4gJT4lIGZpbHRlcihmYXQgIT0gMCkKYGBgCgoKCmBgYHtyfQphdmVyYWdlX2JtaV93ZWlnaHQgPC0gd2VpZ2h0X2xvZ3NfY2xlYW4gJT4lCiAgZ3JvdXBfYnkoaXNfbWFudWFsX3JlcG9ydCkgJT4lCiAgc3VtbWFyaXplKG1lYW5fYm1pID0gbWVhbihibWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIG1lYW5fd2VpZ2h0X3BvdW5kcyA9IG1lYW4od2VpZ2h0X3BvdW5kcywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgZW50cnlfY291bnQgPSBuKCksIAogICAgICAgICAgICAuZ3JvdXBzID0gImtlZXAiKSAKCnByaW50KGF2ZXJhZ2VfYm1pX3dlaWdodCkKCmBgYAoKCgpgYGB7cn0KCgpjcmVhdGVfY3VzdG9tX2JveF9wbG90KHdlaWdodF9sb2dzX2NsZWFuICwgeF9heGlzX2NvbCA9ICJpc19tYW51YWxfcmVwb3J0IiwgeV9heGlzX2NvbCA9ICJ3ZWlnaHRfcG91bmRzIiwgb3JpZW50YXRpb24gPSAiaG9yIiwgY29sb3JzID0gY3VzdG9tX2NvbG9ycykKYGBgCgpgYGB7cn0KIyBVc2UgdGhlIHJlbW92ZV9vdXRsaWVycyBmdW5jdGlvbiB3ZSBjcmVhdGVkIHByZXZpb3VzbHkgdG8gcmVtb3ZlIHRoZSBvdXRsaWVycwoKY29sdW1uc193aXRoX291dGxpZXJzIDwtIGMoIndlaWdodF9wb3VuZHMiLCAiYm1pIikKCgojIExvb3AgdGhyb3VnaCBlYWNoIGNvbHVtbiBhbmQgcmVtb3ZlIG91dGxpZXJzCmZvciAoY29sIGluIGNvbHVtbnNfd2l0aF9vdXRsaWVycykgewogIHdlaWdodF9sb2dzX2NsZWFuIDwtIHJlbW92ZV9vdXRsaWVycyh3ZWlnaHRfbG9nc19jbGVhbiwgY29sKQp9CgpgYGAKCgpgYGB7cn0KZGltKHdlaWdodF9sb2dzX2NsZWFuKQpgYGAKCgoKCgpgYGB7cn0KY3JlYXRlX2N1c3RvbV9ib3hfcGxvdCh3ZWlnaHRfbG9nc19jbGVhbiAsIHhfYXhpc19jb2wgPSAiaXNfbWFudWFsX3JlcG9ydCIsIHlfYXhpc19jb2wgPSAid2VpZ2h0X3BvdW5kcyIsIG9yaWVudGF0aW9uID0gImhvciIsIGNvbG9ycyA9IGN1c3RvbV9jb2xvcnMpCmBgYApPYnNlcnZhdGlvbnM6CgotIE9ubHkgdHdvIHVzZXJzIHJlcG9ydGVkIGZhdCBwZXJjZW50YWdlCi0gSXQgYXBwZWFycyB0aGF0IHVzZXJzIHRoYXQgbG9nIHRoZWlyIHdlaWdodCBkYXRhIG1hbnVhbGx5IGhhdmUgYSBsb3dlciBtZWRpYW4gd2VpZ2h0IHRoYW4gdXNlcnMgdGhhdCBzeW5jZWQgdGhlaXIgd2VpZ2h0IGZyb20gb3RoZXIgZGV2aWNlcy4KLSBVc2VycyB0aGF0IHRoYXQgbG9nIHRoZWlyIHdlaWdodCBkYXRhIG1hbnVhbGwgaGFkIG1vcmUgZW50cmllcwoKCgoKCgojIENhbGN1bGF0ZSB0aGUgdG90YWwgYXZlcmFnZQp0b3RhbF9hdmVyYWdlIDwtIHN1bShhdmVyYWdlcykKCiMgQ2FsY3VsYXRlIHRoZSBwcm9wb3J0aW9ucwpwcm9wb3J0aW9ucyA8LSBhdmVyYWdlcyAvIHRvdGFsX2F2ZXJhZ2UKCiMgQ3JlYXRlIHRoZSBuZXcgZGF0YWZyYW1lIHdpdGggbW9kaWZpZWQgcm93IG5hbWVzCm92ZXJhbGxfYXZlcmFnZV9kZjwtIGRhdGEuZnJhbWUoQXZlcmFnZSA9IGF2ZXJhZ2VzLAogICAgICAgICAgICAgICAgICAgICBQZXJjZW50YWdlID0gcHJvcG9ydGlvbnMgKiAxMDApCgoKCi0gV2VpZ2h0IFJlcG9ydGluZyBCZWhhdmlvcjogQXNzZXNzIHRoZSByZXBvcnRpbmcgYmVoYXZpb3Igb2YgdXNlcnMgdXNpbmcgdGhlIElzTWFudWFsUmVwb3J0IHZhcmlhYmxlLiBDYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2Ugb2YgbWFudWFsIHdlaWdodCByZXBvcnRzIGNvbXBhcmVkIHRvIHRvdGFsIHdlaWdodCByZXBvcnRzIHRvIGRldGVybWluZSBob3cgYWN0aXZlbHkgdXNlcnMgYXJlIHByb3ZpZGluZyB3ZWlnaHQgdXBkYXRlcy4gVGhpcyBjYW4gaW5kaWNhdGUgdXNlciBlbmdhZ2VtZW50IGFuZCBtb3RpdmF0aW9uIHRvIHRyYWNrIHRoZWlyIHdlaWdodC4KCgoKLSBCTUkgZGlzdHJpYnV0aW9uIChwZXJjZW50YWdlIGFib3ZlIG5vcm1hbCBCTUkpCgotIFdlaWdodCBUcmVuZHM6IEFuYWx5emUgdGhlIHRyZW5kcyBpbiB3ZWlnaHQgb3ZlciB0aW1lIChEYXRlKSB0byB1bmRlcnN0YW5kIGlmIHVzZXJzIGFyZSBleHBlcmllbmNpbmcgd2VpZ2h0IGxvc3MsIGdhaW4sIG9yIHN0YWJpbGl0eS4gUGxvdHRpbmcgd2VpZ2h0IChXZWlnaHRLZyBvciBXZWlnaHRQb3VuZHMpIGFnYWluc3QgdGltZSBjYW4gcmV2ZWFsIHBhdHRlcm5zLCBmbHVjdHVhdGlvbnMsIG9yIHNpZ25pZmljYW50IGNoYW5nZXMgaW4gd2VpZ2h0LiBZb3UgY2FuIGFsc28gY2FsY3VsYXRlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3Mgc3VjaCBhcyBhdmVyYWdlIHdlaWdodCwgc3RhbmRhcmQgZGV2aWF0aW9uLCBvciByYXRlIG9mIHdlaWdodCBjaGFuZ2UgdG8gZ2FpbiBpbnNpZ2h0cyBpbnRvIHVzZXIgYmVoYXZpb3IuCgoKClVzZXIgRW5nYWdlbWVudDogVGhlIGRpZmZlcmVuY2UgaW4gZGlzdGluY3QgSURzIGFjcm9zcyBkYXRhc2V0cyBjb3VsZCByZWZsZWN0IGRpZmZlcmVudCBsZXZlbHMgb2YgdXNlciBlbmdhZ2VtZW50IHdpdGggZGlmZmVyZW50IHR5cGVzIG9mIGRhdGEuIEZvciBleGFtcGxlLCB0aGUgIndlaWdodF9sb2dzX2NsZWFuIiBkYXRhc2V0IGhhdmluZyBmZXdlciBkaXN0aW5jdCBJRHMgbWlnaHQgaW5kaWNhdGUgdGhhdCBmZXdlciB1c2VycyBhcmUgYWN0aXZlbHkgbG9nZ2luZyB0aGVpciB3ZWlnaHQgZGF0YSBjb21wYXJlZCB0byBvdGhlciB0eXBlcyBvZiBkYXRhLgoKIyBJbnNpZ2h0cyBhbmQgcmVjb21tZW5kYXRpb25zCgpodHRwczovL3d3dy5jZGMuZ292L21td3Ivdm9sdW1lcy82OC93ci9tbTY4MjNhMS5odG0KCmZpbGU6Ly8vVXNlcnMvdml2aWFuYmFycm9zL0Rlc2t0b3AvUGh5c2ljYWxfQWN0aXZpdHlfR3VpZGVsaW5lc18ybmRfZWRpdGlvbi5wZGYKCgoKIyBBcHBlbmRpeCAKCgoKIwpodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xMzAzNTgzNC9wbG90LWV2ZXJ5LWNvbHVtbi1pbi1hLWRhdGEtZnJhbWUtYXMtYS1oaXN0b2dyYW0tb24tb25lLXBhZ2UtdXNpbmctZ2dwbG90IAoKCgoKaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTMwMzU4MzQvcGxvdC1ldmVyeS1jb2x1bW4taW4tYS1kYXRhLWZyYW1lLWFzLWEtaGlzdG9ncmFtLW9uLW9uZS1wYWdlLXVzaW5nLWdncGxvdAoKCgoKCgoKIyBBbm90aGVyIHNvdXJjZToKaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9hcmFzaG5pYy9maXRiaXQvZGlzY3Vzc2lvbi8zMTM1ODk/c2VhcmNoPWRhdGEKCgojcGFwZXIKaHR0cHM6Ly9kbC5hY20ub3JnL2RvaS9wZGYvMTAuMTE0NS8zMzM5ODI1LjMzOTQ5MjYKCgoKCnRoaXMgaXMgaXQ6CnBoeXNpY2FsIGlubmFjdGl2aXR5LiBQbG90IGEgYmFycGxvdCB3aXRoIHBlcmNlbnRhZ2VzLgpodHRwczovL3d3dy5jZGMuZ292L3BoeXNpY2FsYWN0aXZpdHkvZGF0YS9pbmFjdGl2aXR5LXByZXZhbGVuY2UtbWFwcy9pbmRleC5odG1sI1JhY2UtRXRobmljaXR5CgoKaW5mb3JtYXRpb24gYWJvdXQgcGh5c2ljYWwgYWN0aXZpdHkgZ3VpZGxpbmVzIChzZXggYW5kIGFnZSk6CgpodHRwczovL3d3dy5jZGMuZ292L25jaHMvcHJvZHVjdHMvZGF0YWJyaWVmcy9kYjQ0My5odG0KRWxnYWRkYWwgTiwgS3JhbWFyb3cgRUEsIFJldWJlbiBDLiBQaHlzaWNhbCBhY3Rpdml0eSBhbW9uZyBhZHVsdHMgYWdlZCAxOCBhbmQgb3ZlcjogVW5pdGVkIFN0YXRlcywgMjAyMC4gTkNIUyBEYXRhIEJyaWVmLCBubyA0NDMuIEh5YXR0c3ZpbGxlLCBNRDogTmF0aW9uYWwgQ2VudGVyIGZvciBIZWFsdGggU3RhdGlzdGljcy4gMjAyMi4gRE9JOiBodHRwczovL2R4LmRvaS5vcmcvMTAuMTU2MjAvY2RjOjEyMDIxMyAKCgoKCgoKCgoKCiMgUmVmZXJlbmNlOgotIEVEQTogaHR0cHM6Ly9ycHVicy5jb20vam92aWFsL3IgCgotIEhpc3RvZ3JhbXM6IGh0dHBzOi8vc3RhdGlzdGljc2J5amltLmNvbS9iYXNpY3MvaGlzdG9ncmFtcy8KaHR0cHM6Ly9ibG9nLm1pbml0YWIuY29tL2VuLzMtdGhpbmdzLWEtaGlzdG9ncmFtLWNhbi10ZWxsLXlvdQoKQ2F0ZWdvcmljYWwsIG9yZGluYWwsIGludGVydmFsLCBhbmQgcmF0aW8gdmFyaWFibGVzIDogaHR0cHM6Ly93d3cuZ3JhcGhwYWQuY29tL2d1aWRlcy9wcmlzbS9sYXRlc3Qvc3RhdGlzdGljcy90aGVfZGlmZmVyZW50X2tpbmRzX29mX3ZhcmlhYmwuaHRtCgpBZGQgZGVuc2l0eSBsaW5lIHRvIGhpc3RvZ3JhbTogaHR0cHM6Ly9yLWNvZGVyLmNvbS9kZW5zaXR5LXBsb3QtcgoKLSBFcnJvciBiYXJzIHZzIENJOiBodHRwczovL2Jsb2dzLnNhcy5jb20vY29udGVudC9pbWwvMjAxOS8xMC8wOS9zdGF0aXN0aWMtZXJyb3ItYmFycy1tZWFuLmh0bWwKCgoKCgoKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0gc2hvd2luZyBub3RlYm9vayBpbiBnaXRodWIKCgpjb252ZXJ0IHRvIGp1cHl0ZXIgbm90Ym9vawpvcHRpb246IGh0dHBzOi8vbWVkaXVtLmRhdGFkcml2ZW5pbnZlc3Rvci5jb20vdHJhbnNmb3JtaW5nLXlvdXItcm1kLXRvLWlweW5iLWZpbGUtci1tYXJrZG93bi10by1weXRob24tanVweXRlci1iMTMwNjY0NmY1MGIgCgoKCkhleSBhbGwsCgpTb3JyeSBpZiBJJ20gbWlzdW5kZXJzdGFuZGluZyBoZXJlLCBidXQgSSBoYXZlIGJlZW4ga25pdHRpbmcgdGhlIC5SbWQgbm90ZWJvb2sgdG8gYSAubWQgZmlsZSB3aXRoaW4gUlN0dWRpbywgYW5kIGl0IHNlZW1zIHRvIGRpc3BsYXkgdmVyeSB3ZWxsIGluIEdpdEh1Yi4gWW91IGNhbiBzZWUgYW4gZXhhbXBsZSBpbiBteSByZXBvIHRvIHNlZSBpZiBJJ20gb24gdHJhY2sgd2l0aCB0aGlzIHRocmVhZC4KClRoZSBsaW5rcyBiZWxvdyBnaXZlIHRoZSBleHBsYW5hdGlvbi4gU2hvcnQgVmVyc2lvbjoKCmNoYW5nZSAib3V0cHV0PWh0bWxfZG9jdW1lbnQiIHRvICJvdXRwdXQ9Z2l0aHViX2RvY3VtZW50IgoKCgoKCmtuaXQgdGhlIGRvY3VtZW50CnB1c2ggdGhlIC5tZCBmaWxlIHRvIEdpdEh1YiBpbnN0ZWFkIG9mIHRoZSAuUm1kCmJlIHN1cmUgdG8gcHVzaCB0aGUgJ19maWxlcycgZm9sZGVyIHRvIGluY2x1ZGUgYW55IGltYWdlcwpodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9naXRodWJfZG9jdW1lbnRfZm9ybWF0Lmh0bWwKaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vSm9zaHVhVFBpZXJjZS9iOTE5MTY4NDIxYjQwZTA2NDgxMDgwZWI1M2MzZmIyZg==